@netoalmanca/advpl-sensei 1.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.
- package/agents/changelog-generator.md +63 -0
- package/agents/code-generator.md +215 -0
- package/agents/code-reviewer.md +145 -0
- package/agents/debugger.md +83 -0
- package/agents/doc-generator.md +67 -0
- package/agents/docs-reference.md +86 -0
- package/agents/migrator.md +84 -0
- package/agents/process-consultant.md +97 -0
- package/agents/refactorer.md +75 -0
- package/agents/sx-configurator.md +67 -0
- package/commands/changelog.md +66 -0
- package/commands/diagnose.md +67 -0
- package/commands/docs.md +81 -0
- package/commands/document.md +67 -0
- package/commands/explain.md +60 -0
- package/commands/generate.md +111 -0
- package/commands/migrate.md +81 -0
- package/commands/process.md +111 -0
- package/commands/refactor.md +65 -0
- package/commands/review.md +60 -0
- package/commands/sxgen.md +98 -0
- package/commands/test.md +103 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
- package/skills/advpl-code-generation/SKILL.md +163 -0
- package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
- package/skills/advpl-code-generation/patterns-jobs.md +519 -0
- package/skills/advpl-code-generation/patterns-mvc.md +765 -0
- package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
- package/skills/advpl-code-generation/patterns-rest.md +974 -0
- package/skills/advpl-code-generation/patterns-soap.md +639 -0
- package/skills/advpl-code-generation/patterns-treport.md +481 -0
- package/skills/advpl-code-generation/patterns-workflow.md +779 -0
- package/skills/advpl-code-generation/templates-classes.md +1096 -0
- package/skills/advpl-code-review/SKILL.md +72 -0
- package/skills/advpl-code-review/rules-best-practices.md +444 -0
- package/skills/advpl-code-review/rules-modernization.md +290 -0
- package/skills/advpl-code-review/rules-performance.md +333 -0
- package/skills/advpl-code-review/rules-security.md +302 -0
- package/skills/advpl-debugging/SKILL.md +265 -0
- package/skills/advpl-debugging/common-errors.md +1124 -0
- package/skills/advpl-debugging/performance-tips.md +768 -0
- package/skills/advpl-refactoring/SKILL.md +139 -0
- package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
- package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
- package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
- package/skills/changelog-patterns/SKILL.md +99 -0
- package/skills/code-explanation/SKILL.md +66 -0
- package/skills/documentation-patterns/SKILL.md +172 -0
- package/skills/embedded-sql/SKILL.md +379 -0
- package/skills/probat-testing/SKILL.md +226 -0
- package/skills/probat-testing/patterns-unit-tests.md +614 -0
- package/skills/protheus-business/SKILL.md +92 -0
- package/skills/protheus-business/modulo-compras.md +780 -0
- package/skills/protheus-business/modulo-contabilidade.md +874 -0
- package/skills/protheus-business/modulo-estoque.md +876 -0
- package/skills/protheus-business/modulo-faturamento.md +800 -0
- package/skills/protheus-business/modulo-financeiro.md +1015 -0
- package/skills/protheus-business/modulo-fiscal.md +749 -0
- package/skills/protheus-business/modulo-manutencao.md +848 -0
- package/skills/protheus-business/modulo-pcp.md +743 -0
- package/skills/protheus-reference/SKILL.md +119 -0
- package/skills/protheus-reference/native-functions.md +7029 -0
- package/skills/protheus-reference/rest-api-reference.md +1758 -0
- package/skills/protheus-reference/restricted-functions.md +265 -0
- package/skills/protheus-reference/sx-dictionary.md +854 -0
- package/skills/sx-configuration/SKILL.md +184 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
# ProBat Unit Test Patterns
|
|
2
|
+
|
|
3
|
+
Templates and patterns for writing ProBat tests in TLPP. All examples are based on the official TOTVS `tlpp-probat-samples` repository.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Naming Convention
|
|
8
|
+
|
|
9
|
+
- Test files: `test_<target_name>.tlpp`
|
|
10
|
+
- Test classes: `Test<TargetName>` (e.g., `TestClienteService`)
|
|
11
|
+
- Test functions: `U_test<TargetName>` (e.g., `U_testCalculatePercent`)
|
|
12
|
+
- Test methods: descriptive name starting with `test` (e.g., `testInsert`, `testUpdate`, `testInvalidInput`)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Function Test (Basic)
|
|
17
|
+
|
|
18
|
+
The simplest ProBat test: a function with `@TestFixture` that calls the target and asserts the result.
|
|
19
|
+
|
|
20
|
+
```tlpp
|
|
21
|
+
#include "tlpp-probat.th"
|
|
22
|
+
|
|
23
|
+
namespace test.mymodule
|
|
24
|
+
|
|
25
|
+
@TestFixture(owner='myteam', target='my_source.tlpp')
|
|
26
|
+
function U_testMyFunction() as logical
|
|
27
|
+
|
|
28
|
+
local cInput := 'test_value' as character
|
|
29
|
+
local xValue := mymodule.U_myFunction(cInput)
|
|
30
|
+
local xExpected := 'expected_result'
|
|
31
|
+
|
|
32
|
+
tlpp.probat.assertEquals(xValue, xExpected, 'myFunction should return expected result')
|
|
33
|
+
|
|
34
|
+
return .T.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Key points:**
|
|
38
|
+
- `#include "tlpp-probat.th"` is mandatory
|
|
39
|
+
- The function must return `.T.`
|
|
40
|
+
- Use `target` property to link the test to its source file
|
|
41
|
+
- Assertions can use full qualified name `tlpp.probat.assertEquals` or `using namespace tlpp.probat`
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 2. Class Test (Simple)
|
|
46
|
+
|
|
47
|
+
Testing a class method with a class-based test fixture.
|
|
48
|
+
|
|
49
|
+
```tlpp
|
|
50
|
+
#include "tlpp-core.th"
|
|
51
|
+
#include "tlpp-probat.th"
|
|
52
|
+
|
|
53
|
+
namespace test.mymodule
|
|
54
|
+
|
|
55
|
+
using namespace tlpp.probat
|
|
56
|
+
|
|
57
|
+
@TestFixture(owner='myteam', target='cliente_service.tlpp')
|
|
58
|
+
class TestClienteService
|
|
59
|
+
|
|
60
|
+
private data cCodigo as character
|
|
61
|
+
|
|
62
|
+
public method new() constructor
|
|
63
|
+
|
|
64
|
+
@Test('should validate client code format')
|
|
65
|
+
public method testValidateCode()
|
|
66
|
+
|
|
67
|
+
@Test('should return client name by code')
|
|
68
|
+
public method testGetClientName()
|
|
69
|
+
|
|
70
|
+
endclass
|
|
71
|
+
|
|
72
|
+
method new() class TestClienteService
|
|
73
|
+
::cCodigo := '000001'
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
method testValidateCode() class TestClienteService
|
|
77
|
+
|
|
78
|
+
local lResult := mymodule.validateCode(::cCodigo)
|
|
79
|
+
|
|
80
|
+
assertTrue(lResult, 'valid code should return true')
|
|
81
|
+
|
|
82
|
+
return .T.
|
|
83
|
+
|
|
84
|
+
method testGetClientName() class TestClienteService
|
|
85
|
+
|
|
86
|
+
local cName := mymodule.getClientName(::cCodigo)
|
|
87
|
+
|
|
88
|
+
assertNotEquals(cName, '', 'client name should not be empty')
|
|
89
|
+
assertIsContained(cName, ' ', 'client name should contain space')
|
|
90
|
+
|
|
91
|
+
return .T.
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Class Test with Setup/TearDown (Full Lifecycle)
|
|
97
|
+
|
|
98
|
+
Complete test with initialization, per-test setup, and cleanup.
|
|
99
|
+
|
|
100
|
+
```tlpp
|
|
101
|
+
#include "tlpp-core.th"
|
|
102
|
+
#include "tlpp-probat.th"
|
|
103
|
+
|
|
104
|
+
namespace test.mymodule
|
|
105
|
+
|
|
106
|
+
using namespace tlpp.probat
|
|
107
|
+
|
|
108
|
+
@TestFixture(owner='myteam')
|
|
109
|
+
class TestPedidoService
|
|
110
|
+
|
|
111
|
+
private data nTotal as numeric
|
|
112
|
+
private data cPedido as character
|
|
113
|
+
private data lSetup as logical
|
|
114
|
+
|
|
115
|
+
public method new() constructor
|
|
116
|
+
|
|
117
|
+
// Runs ONCE before all tests
|
|
118
|
+
@OneTimeSetUp()
|
|
119
|
+
public method initEnvironment()
|
|
120
|
+
|
|
121
|
+
// Runs BEFORE EACH test
|
|
122
|
+
@Setup()
|
|
123
|
+
public method resetValues()
|
|
124
|
+
|
|
125
|
+
// Tests
|
|
126
|
+
@Test('should calculate order total correctly')
|
|
127
|
+
public method testCalculateTotal()
|
|
128
|
+
|
|
129
|
+
@Test('should apply discount when applicable')
|
|
130
|
+
public method testApplyDiscount()
|
|
131
|
+
|
|
132
|
+
// Runs AFTER EACH test
|
|
133
|
+
@TearDown()
|
|
134
|
+
public method clearValues()
|
|
135
|
+
|
|
136
|
+
// Runs ONCE after all tests
|
|
137
|
+
@OneTimeTearDown()
|
|
138
|
+
public method cleanupEnvironment()
|
|
139
|
+
|
|
140
|
+
endclass
|
|
141
|
+
|
|
142
|
+
method new() class TestPedidoService
|
|
143
|
+
::nTotal := 0
|
|
144
|
+
::cPedido := ''
|
|
145
|
+
::lSetup := .F.
|
|
146
|
+
return self
|
|
147
|
+
|
|
148
|
+
method initEnvironment() class TestPedidoService
|
|
149
|
+
// One-time initialization (e.g., open connection, prepare data)
|
|
150
|
+
::lSetup := .T.
|
|
151
|
+
conout('-> [OneTimeSetUp] Environment initialized')
|
|
152
|
+
return .T.
|
|
153
|
+
|
|
154
|
+
method resetValues() class TestPedidoService
|
|
155
|
+
// Reset state before each test
|
|
156
|
+
::nTotal := 0
|
|
157
|
+
::cPedido := ''
|
|
158
|
+
conout(' -> [Setup] Values reset')
|
|
159
|
+
return .T.
|
|
160
|
+
|
|
161
|
+
method testCalculateTotal() class TestPedidoService
|
|
162
|
+
::nTotal := mymodule.calculateTotal(100, 3) // price * qty
|
|
163
|
+
assertEquals(::nTotal, 300, 'total should be price * quantity')
|
|
164
|
+
return .T.
|
|
165
|
+
|
|
166
|
+
method testApplyDiscount() class TestPedidoService
|
|
167
|
+
::nTotal := mymodule.applyDiscount(1000, 10) // value, percent
|
|
168
|
+
assertEquals(::nTotal, 900, 'should apply 10% discount')
|
|
169
|
+
assertLess(::nTotal, 1000, 'discounted total should be less than original')
|
|
170
|
+
return .T.
|
|
171
|
+
|
|
172
|
+
method clearValues() class TestPedidoService
|
|
173
|
+
::nTotal := 0
|
|
174
|
+
::cPedido := ''
|
|
175
|
+
conout(' -> [TearDown] Values cleared')
|
|
176
|
+
return .T.
|
|
177
|
+
|
|
178
|
+
method cleanupEnvironment() class TestPedidoService
|
|
179
|
+
::lSetup := .F.
|
|
180
|
+
conout('-> [OneTimeTearDown] Environment cleaned up')
|
|
181
|
+
return .T.
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 4. REST Endpoint Test
|
|
187
|
+
|
|
188
|
+
Testing a REST API using `tlpp.rest.runTestSimple` from tlppCore.
|
|
189
|
+
|
|
190
|
+
```tlpp
|
|
191
|
+
#include "tlpp-core.th"
|
|
192
|
+
#include "tlpp-probat.th"
|
|
193
|
+
|
|
194
|
+
namespace test.api
|
|
195
|
+
|
|
196
|
+
using namespace tlpp.probat
|
|
197
|
+
|
|
198
|
+
@TestFixture(owner='myteam')
|
|
199
|
+
function U_testCustomerAPI() as logical
|
|
200
|
+
|
|
201
|
+
local cEndPoint := '/api/v1/customers'
|
|
202
|
+
local cMethod := 'GET'
|
|
203
|
+
local cBodyOut := ''
|
|
204
|
+
local cHeadRet := ''
|
|
205
|
+
local cBodyRet := ''
|
|
206
|
+
local nStatusCode := 0
|
|
207
|
+
local nServiceRet := -1
|
|
208
|
+
|
|
209
|
+
// Execute the REST endpoint
|
|
210
|
+
nServiceRet := tlpp.rest.runTestSimple( ;
|
|
211
|
+
@cHeadRet, ;
|
|
212
|
+
@cBodyRet, ;
|
|
213
|
+
@nStatusCode, ;
|
|
214
|
+
cEndPoint, ;
|
|
215
|
+
cMethod, ;
|
|
216
|
+
cBodyOut ;
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if (nServiceRet >= 0)
|
|
220
|
+
|
|
221
|
+
// Validate HTTP status
|
|
222
|
+
assertEquals(nStatusCode, 200, 'should return HTTP 200')
|
|
223
|
+
|
|
224
|
+
// Validate response header
|
|
225
|
+
assertIsRegexPartial(cHeadRet, "HTTP.([\w:\/.]+)([200 OK]+)", 'header should contain 200 OK')
|
|
226
|
+
|
|
227
|
+
// Validate response body
|
|
228
|
+
assertJson(cBodyRet, '{"success":true}', 'response should contain success flag')
|
|
229
|
+
|
|
230
|
+
else
|
|
231
|
+
assertError('could not execute the REST service')
|
|
232
|
+
endif
|
|
233
|
+
|
|
234
|
+
return .T.
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### REST POST Test
|
|
238
|
+
|
|
239
|
+
```tlpp
|
|
240
|
+
#include "tlpp-core.th"
|
|
241
|
+
#include "tlpp-probat.th"
|
|
242
|
+
|
|
243
|
+
namespace test.api
|
|
244
|
+
|
|
245
|
+
using namespace tlpp.probat
|
|
246
|
+
|
|
247
|
+
@TestFixture(owner='myteam')
|
|
248
|
+
function U_testCustomerPost() as logical
|
|
249
|
+
|
|
250
|
+
local cEndPoint := '/api/v1/customers'
|
|
251
|
+
local cMethod := 'POST'
|
|
252
|
+
local cBodyOut := '{"code":"000001","name":"Test Customer","store":"01"}'
|
|
253
|
+
local cHeadRet := ''
|
|
254
|
+
local cBodyRet := ''
|
|
255
|
+
local nStatusCode := 0
|
|
256
|
+
local nServiceRet := -1
|
|
257
|
+
|
|
258
|
+
nServiceRet := tlpp.rest.runTestSimple( ;
|
|
259
|
+
@cHeadRet, ;
|
|
260
|
+
@cBodyRet, ;
|
|
261
|
+
@nStatusCode, ;
|
|
262
|
+
cEndPoint, ;
|
|
263
|
+
cMethod, ;
|
|
264
|
+
cBodyOut ;
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
if (nServiceRet >= 0)
|
|
268
|
+
assertEquals(nStatusCode, 201, 'should return HTTP 201 Created')
|
|
269
|
+
else
|
|
270
|
+
assertError('could not execute the REST service')
|
|
271
|
+
endif
|
|
272
|
+
|
|
273
|
+
return .T.
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## 5. Data Validation Patterns
|
|
279
|
+
|
|
280
|
+
### Testing Multiple Scenarios
|
|
281
|
+
|
|
282
|
+
```tlpp
|
|
283
|
+
#include "tlpp-core.th"
|
|
284
|
+
#include "tlpp-probat.th"
|
|
285
|
+
|
|
286
|
+
namespace test.mymodule
|
|
287
|
+
|
|
288
|
+
using namespace tlpp.probat
|
|
289
|
+
|
|
290
|
+
@TestFixture(owner='myteam')
|
|
291
|
+
function U_testInputValidation() as logical
|
|
292
|
+
|
|
293
|
+
local xValue
|
|
294
|
+
local xExpected
|
|
295
|
+
local cError := '' as character
|
|
296
|
+
|
|
297
|
+
// Valid input
|
|
298
|
+
if (mymodule.U_validate(100, 10, @xValue, @cError))
|
|
299
|
+
xExpected := 10
|
|
300
|
+
assertEquals(xValue, xExpected, 'valid input should return correct result')
|
|
301
|
+
else
|
|
302
|
+
assertError('valid input should not fail: ' + cError)
|
|
303
|
+
endif
|
|
304
|
+
|
|
305
|
+
// Invalid input - string instead of number
|
|
306
|
+
if (mymodule.U_validate('', 10, @xValue, @cError))
|
|
307
|
+
assertError('invalid input should not return success')
|
|
308
|
+
else
|
|
309
|
+
xExpected := 'Invalid value!'
|
|
310
|
+
assertEquals(cError, xExpected, 'should return correct error message')
|
|
311
|
+
endif
|
|
312
|
+
|
|
313
|
+
// Boundary value - zero
|
|
314
|
+
if (mymodule.U_validate(0, 10, @xValue, @cError))
|
|
315
|
+
xExpected := 0
|
|
316
|
+
assertEquals(xValue, xExpected, 'zero input should return zero')
|
|
317
|
+
else
|
|
318
|
+
assertError('zero should be a valid input')
|
|
319
|
+
endif
|
|
320
|
+
|
|
321
|
+
return .T.
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Testing with Arrays and JSON
|
|
325
|
+
|
|
326
|
+
```tlpp
|
|
327
|
+
#include "tlpp-core.th"
|
|
328
|
+
#include "tlpp-probat.th"
|
|
329
|
+
|
|
330
|
+
namespace test.mymodule
|
|
331
|
+
|
|
332
|
+
using namespace tlpp.probat
|
|
333
|
+
|
|
334
|
+
@TestFixture(owner='myteam')
|
|
335
|
+
function U_testDataStructures() as logical
|
|
336
|
+
|
|
337
|
+
local aResult := {1, 2, 3, 'four', Nil, 6}
|
|
338
|
+
local aExpected := {1, 2, 3, 'four', Nil, 6}
|
|
339
|
+
local oJson1, oJson2
|
|
340
|
+
|
|
341
|
+
// Array comparison
|
|
342
|
+
assertVector(aResult, aExpected, 'arrays should be equal')
|
|
343
|
+
|
|
344
|
+
// JSON object comparison
|
|
345
|
+
oJson1 := JsonObject():New()
|
|
346
|
+
oJson1:fromJson('{"key":"value","num":42}')
|
|
347
|
+
|
|
348
|
+
oJson2 := JsonObject():New()
|
|
349
|
+
oJson2:fromJson('{"key":"value","num":42}')
|
|
350
|
+
|
|
351
|
+
assertJson(oJson1, oJson2, 'JSON objects should be equal')
|
|
352
|
+
|
|
353
|
+
// JSON object vs string
|
|
354
|
+
assertJson(oJson1, '{"key":"value","num":42}', 'JSON should match string representation')
|
|
355
|
+
|
|
356
|
+
// Nil check
|
|
357
|
+
assertNil(, 'nil value should be nil')
|
|
358
|
+
|
|
359
|
+
return .T.
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
## 6. Skip Patterns
|
|
365
|
+
|
|
366
|
+
### Skip Entire Test
|
|
367
|
+
|
|
368
|
+
```tlpp
|
|
369
|
+
@TestFixture(owner='myteam')
|
|
370
|
+
@Skip()
|
|
371
|
+
function U_testNotReady() as logical
|
|
372
|
+
assertError('this should never execute')
|
|
373
|
+
return .T.
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
### Skip by Environment
|
|
377
|
+
|
|
378
|
+
```tlpp
|
|
379
|
+
// Skip on Windows
|
|
380
|
+
@TestFixture(owner='myteam')
|
|
381
|
+
@Skip(system="windows")
|
|
382
|
+
function U_testLinuxOnly() as logical
|
|
383
|
+
// ...
|
|
384
|
+
return .T.
|
|
385
|
+
|
|
386
|
+
// Skip on specific AppServer
|
|
387
|
+
@TestFixture(owner='myteam')
|
|
388
|
+
@Skip(appServerName="HARPIA")
|
|
389
|
+
function U_testNotOnHarpia() as logical
|
|
390
|
+
// ...
|
|
391
|
+
return .T.
|
|
392
|
+
|
|
393
|
+
// Skip based on TLPP version
|
|
394
|
+
@TestFixture(owner='myteam')
|
|
395
|
+
@Skip(tlppVersion=">= 01.02.10")
|
|
396
|
+
function U_testOlderVersionOnly() as logical
|
|
397
|
+
// ...
|
|
398
|
+
return .T.
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Skip Individual Assertions (SKIPASSERT)
|
|
402
|
+
|
|
403
|
+
```tlpp
|
|
404
|
+
@TestFixture(owner='myteam')
|
|
405
|
+
function U_testWithConditionalSkip() as logical
|
|
406
|
+
|
|
407
|
+
// Skip next assertion unconditionally
|
|
408
|
+
SKIPASSERT
|
|
409
|
+
assertError('this assert is skipped')
|
|
410
|
+
|
|
411
|
+
// Skip next assertion on Windows
|
|
412
|
+
SKIPASSERT SYSTEM "windows"
|
|
413
|
+
assertError('skipped on windows')
|
|
414
|
+
|
|
415
|
+
// This assertion always runs
|
|
416
|
+
assertOK('this always runs')
|
|
417
|
+
|
|
418
|
+
return .T.
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## 7. Suite Organization
|
|
424
|
+
|
|
425
|
+
Group tests into suites for selective execution:
|
|
426
|
+
|
|
427
|
+
```tlpp
|
|
428
|
+
@TestFixture(owner='myteam', suite='financeiro')
|
|
429
|
+
function U_testContasReceber() as logical
|
|
430
|
+
// ...
|
|
431
|
+
return .T.
|
|
432
|
+
|
|
433
|
+
@TestFixture(owner='myteam', suite='financeiro')
|
|
434
|
+
function U_testContasPagar() as logical
|
|
435
|
+
// ...
|
|
436
|
+
return .T.
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 8. Expected Error Test
|
|
442
|
+
|
|
443
|
+
When testing code that should produce an error:
|
|
444
|
+
|
|
445
|
+
```tlpp
|
|
446
|
+
@TestFixture(owner='myteam', target='risky_operation.tlpp')
|
|
447
|
+
@ErrorLog('type mismatch')
|
|
448
|
+
function U_testExpectedError() as logical
|
|
449
|
+
mymodule.U_riskyOperation()
|
|
450
|
+
return .T.
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Class-based:
|
|
454
|
+
|
|
455
|
+
```tlpp
|
|
456
|
+
@TestFixture(owner='myteam')
|
|
457
|
+
class TestErrorHandling
|
|
458
|
+
|
|
459
|
+
public method new() constructor
|
|
460
|
+
|
|
461
|
+
@Test('should handle type mismatch gracefully')
|
|
462
|
+
@ErrorLog('type mismatch')
|
|
463
|
+
public method testTypeMismatch()
|
|
464
|
+
|
|
465
|
+
endclass
|
|
466
|
+
|
|
467
|
+
method new() class TestErrorHandling
|
|
468
|
+
return self
|
|
469
|
+
|
|
470
|
+
method testTypeMismatch() class TestErrorHandling
|
|
471
|
+
mymodule.U_riskyOperation()
|
|
472
|
+
return .T.
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
## 9. Integration Test with Database
|
|
478
|
+
|
|
479
|
+
Pattern for tests that require database access.
|
|
480
|
+
|
|
481
|
+
```tlpp
|
|
482
|
+
#include "tlpp-core.th"
|
|
483
|
+
#include "tlpp-probat.th"
|
|
484
|
+
|
|
485
|
+
namespace test.integration
|
|
486
|
+
|
|
487
|
+
using namespace tlpp.probat
|
|
488
|
+
|
|
489
|
+
@TestFixture(suite='database')
|
|
490
|
+
class TestDatabaseOperations
|
|
491
|
+
|
|
492
|
+
private data nConn as numeric
|
|
493
|
+
private data lConn as logical
|
|
494
|
+
private data cTable as character
|
|
495
|
+
private data cAlias as character
|
|
496
|
+
|
|
497
|
+
public method new() constructor
|
|
498
|
+
|
|
499
|
+
@OneTimeSetUp()
|
|
500
|
+
public method openConnection()
|
|
501
|
+
|
|
502
|
+
@Setup()
|
|
503
|
+
public method prepareTable()
|
|
504
|
+
|
|
505
|
+
@Test('should insert record into table')
|
|
506
|
+
public method testInsert()
|
|
507
|
+
|
|
508
|
+
@Test('should update existing record')
|
|
509
|
+
public method testUpdate()
|
|
510
|
+
|
|
511
|
+
@TearDown()
|
|
512
|
+
public method clearData()
|
|
513
|
+
|
|
514
|
+
@OneTimeTearDown()
|
|
515
|
+
public method closeConnection()
|
|
516
|
+
|
|
517
|
+
endclass
|
|
518
|
+
|
|
519
|
+
method new() class TestDatabaseOperations
|
|
520
|
+
::nConn := -1
|
|
521
|
+
::lConn := .F.
|
|
522
|
+
::cTable := 'TEST_TBL'
|
|
523
|
+
::cAlias := 'TST'
|
|
524
|
+
return self
|
|
525
|
+
|
|
526
|
+
method openConnection() class TestDatabaseOperations
|
|
527
|
+
local cEnv := GetEnvServer()
|
|
528
|
+
local cIni := GetSrvIniName()
|
|
529
|
+
local cDB := GetPvProfString(cEnv, 'DBDATABASE', '', cIni)
|
|
530
|
+
local cDsn := GetPvProfString(cEnv, 'DBALIAS', '', cIni)
|
|
531
|
+
local cSrv := GetPvProfString(cEnv, 'DBSERVER', '', cIni)
|
|
532
|
+
local nPort := Val(GetPvProfString(cEnv, 'DBPORT', '', cIni))
|
|
533
|
+
|
|
534
|
+
::nConn := TcLink(cDB + '/' + cDsn, cSrv, nPort)
|
|
535
|
+
::lConn := (::nConn >= 0)
|
|
536
|
+
|
|
537
|
+
assertTrue(::lConn, 'database connection should succeed')
|
|
538
|
+
return .T.
|
|
539
|
+
|
|
540
|
+
method prepareTable() class TestDatabaseOperations
|
|
541
|
+
// Create or clean test table before each test
|
|
542
|
+
if (!TCCanOpen(::cTable))
|
|
543
|
+
local aFields := { ;
|
|
544
|
+
{'ID', 'C', 5, 0}, ;
|
|
545
|
+
{'DESCR', 'C', 50, 0} ;
|
|
546
|
+
}
|
|
547
|
+
dbCreate(::cTable, aFields, 'TOPCONN')
|
|
548
|
+
endif
|
|
549
|
+
|
|
550
|
+
assertTrue(TCCanOpen(::cTable), 'table should exist')
|
|
551
|
+
dbUseArea(.T., 'TOPCONN', ::cTable, ::cAlias, .T., .F.)
|
|
552
|
+
return .T.
|
|
553
|
+
|
|
554
|
+
method testInsert() class TestDatabaseOperations
|
|
555
|
+
dbSelectArea(::cAlias)
|
|
556
|
+
dbAppend(.T.)
|
|
557
|
+
(::cAlias)->ID := '00001'
|
|
558
|
+
(::cAlias)->DESCR := 'Test Record'
|
|
559
|
+
dbCommit()
|
|
560
|
+
dbRUnLock(RecNo())
|
|
561
|
+
|
|
562
|
+
assertEquals((::cAlias)->ID, '00001', 'inserted ID should match')
|
|
563
|
+
return .T.
|
|
564
|
+
|
|
565
|
+
method testUpdate() class TestDatabaseOperations
|
|
566
|
+
dbSelectArea(::cAlias)
|
|
567
|
+
dbRLock(RecNo())
|
|
568
|
+
(::cAlias)->DESCR := 'Updated Record'
|
|
569
|
+
dbCommit()
|
|
570
|
+
dbRUnLock(RecNo())
|
|
571
|
+
|
|
572
|
+
assertEquals((::cAlias)->DESCR, 'Updated Record', 'updated description should match')
|
|
573
|
+
return .T.
|
|
574
|
+
|
|
575
|
+
method clearData() class TestDatabaseOperations
|
|
576
|
+
TCCommit(1)
|
|
577
|
+
dbSelectArea(::cAlias)
|
|
578
|
+
TcSqlExec('DELETE FROM ' + ::cTable)
|
|
579
|
+
TCCommit(2)
|
|
580
|
+
TCCommit(4)
|
|
581
|
+
return .T.
|
|
582
|
+
|
|
583
|
+
method closeConnection() class TestDatabaseOperations
|
|
584
|
+
if (::lConn)
|
|
585
|
+
dbSelectArea(::cAlias)
|
|
586
|
+
if (TCCanOpen(::cTable))
|
|
587
|
+
dbCloseArea()
|
|
588
|
+
TCDelFile(::cTable)
|
|
589
|
+
endif
|
|
590
|
+
TCUNLink(::nConn)
|
|
591
|
+
endif
|
|
592
|
+
return .T.
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
---
|
|
596
|
+
|
|
597
|
+
## Recommended Project Structure
|
|
598
|
+
|
|
599
|
+
```
|
|
600
|
+
project/
|
|
601
|
+
src/
|
|
602
|
+
mymodule/
|
|
603
|
+
cliente_service.tlpp
|
|
604
|
+
pedido_service.tlpp
|
|
605
|
+
test/
|
|
606
|
+
unit/
|
|
607
|
+
mymodule/
|
|
608
|
+
test_cliente_service.tlpp
|
|
609
|
+
test_pedido_service.tlpp
|
|
610
|
+
api/
|
|
611
|
+
test_customer_api.tlpp
|
|
612
|
+
integration/
|
|
613
|
+
test_database.tlpp
|
|
614
|
+
```
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: protheus-business
|
|
3
|
+
description: Use when consulting Protheus ERP business processes, module workflows, routines, integrations, or understanding how business operations work in TOTVS Protheus
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Protheus Business Processes
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Reference guide for TOTVS Protheus ERP business processes. Provides understanding of how each module works, its routines, tables, business rules, and how modules integrate with each other.
|
|
11
|
+
|
|
12
|
+
## When to Use
|
|
13
|
+
|
|
14
|
+
- Understanding business processes within a Protheus module
|
|
15
|
+
- Looking up module functions, routines, and workflows
|
|
16
|
+
- Finding which tables and fields a process uses
|
|
17
|
+
- Understanding ERP integrations between modules
|
|
18
|
+
- Questions like "how does faturamento work?", "what tables does compras use?", "how do compras and estoque integrate?"
|
|
19
|
+
|
|
20
|
+
## Module Index
|
|
21
|
+
|
|
22
|
+
| Module | Prefix | Reference File |
|
|
23
|
+
|--------|--------|----------------|
|
|
24
|
+
| Compras | COM | modulo-compras.md |
|
|
25
|
+
| Estoque | EST | modulo-estoque.md |
|
|
26
|
+
| Faturamento | FAT | modulo-faturamento.md |
|
|
27
|
+
| Financeiro | FIN | modulo-financeiro.md |
|
|
28
|
+
| Contabilidade | CTB | modulo-contabilidade.md |
|
|
29
|
+
| Fiscal | FIS | modulo-fiscal.md |
|
|
30
|
+
| PCP | PCP | modulo-pcp.md |
|
|
31
|
+
| Manutencao de Ativos | MNT | modulo-manutencao.md |
|
|
32
|
+
|
|
33
|
+
## Lookup Strategy
|
|
34
|
+
|
|
35
|
+
```dot
|
|
36
|
+
digraph lookup {
|
|
37
|
+
"Business process query?" [shape=diamond];
|
|
38
|
+
"Identify module(s)" [shape=box];
|
|
39
|
+
"Check module reference file" [shape=box];
|
|
40
|
+
"Found?" [shape=diamond];
|
|
41
|
+
"Return result" [shape=box];
|
|
42
|
+
"Search TDN online" [shape=box];
|
|
43
|
+
"WebSearch: site:tdn.totvs.com <term>" [shape=box];
|
|
44
|
+
"Fetch failed?" [shape=diamond];
|
|
45
|
+
"Playwright fallback" [shape=box];
|
|
46
|
+
|
|
47
|
+
"Business process query?" -> "Identify module(s)";
|
|
48
|
+
"Identify module(s)" -> "Check module reference file";
|
|
49
|
+
"Check module reference file" -> "Found?";
|
|
50
|
+
"Found?" -> "Return result" [label="yes"];
|
|
51
|
+
"Found?" -> "Search TDN online" [label="no"];
|
|
52
|
+
"Search TDN online" -> "WebSearch: site:tdn.totvs.com <term>";
|
|
53
|
+
"WebSearch: site:tdn.totvs.com <term>" -> "Fetch failed?";
|
|
54
|
+
"Fetch failed?" -> "Return result" [label="no"];
|
|
55
|
+
"Fetch failed?" -> "Playwright fallback" [label="yes"];
|
|
56
|
+
"Playwright fallback" -> "Return result";
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
1. **Identify module(s):** Determine which module(s) the query relates to using the Module Index above
|
|
61
|
+
2. **Local first:** Check the corresponding module reference file (e.g., modulo-compras.md)
|
|
62
|
+
3. **Online fallback:** Search TDN with `WebSearch` using query: `site:tdn.totvs.com <module> <process> protheus`
|
|
63
|
+
4. **WebFetch TDN page:** If URL found, use `WebFetch` to extract details
|
|
64
|
+
5. **Playwright fallback:** Se `WebSearch` ou `WebFetch` falhar, use Playwright MCP:
|
|
65
|
+
- `browser_navigate` para a URL do TDN (se disponivel) ou buscar em `https://tdn.totvs.com`
|
|
66
|
+
- `browser_snapshot` para extrair texto; se insuficiente, `browser_take_screenshot` para captura visual
|
|
67
|
+
- `browser_close` ao finalizar para liberar recursos
|
|
68
|
+
|
|
69
|
+
## Response Format
|
|
70
|
+
|
|
71
|
+
Adaptive based on query type:
|
|
72
|
+
|
|
73
|
+
| Query Type | Response Structure |
|
|
74
|
+
|------------|-------------------|
|
|
75
|
+
| Process | Description -> Routines -> Tables/Fields -> Step-by-step flow -> Integrations -> Entry points |
|
|
76
|
+
| Routine | What it does -> Tables it moves -> Parameters -> Process it belongs to |
|
|
77
|
+
| Module | Overview -> Main tables -> Main routines -> Key processes -> Integrations |
|
|
78
|
+
| Integration | Flow between modules -> Linking tables -> Routines involved |
|
|
79
|
+
|
|
80
|
+
## Online Search Tips
|
|
81
|
+
|
|
82
|
+
When searching TDN (TOTVS Developer Network):
|
|
83
|
+
- Use `WebSearch` with query: `site:tdn.totvs.com <module> <process> protheus`
|
|
84
|
+
- For routine docs: `site:tdn.totvs.com <routine_name> advpl protheus`
|
|
85
|
+
- For module integration: `site:tdn.totvs.com integracao <module_a> <module_b> protheus`
|
|
86
|
+
- TDN base URL: `https://tdn.totvs.com`
|
|
87
|
+
|
|
88
|
+
## Cross-References
|
|
89
|
+
|
|
90
|
+
- **protheus-reference** — for native function details (syntax, parameters, return values)
|
|
91
|
+
- **advpl-code-generation** — for code patterns and templates
|
|
92
|
+
- **embedded-sql** — for query examples using Embedded SQL
|