@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.
Files changed (70) hide show
  1. package/agents/changelog-generator.md +63 -0
  2. package/agents/code-generator.md +215 -0
  3. package/agents/code-reviewer.md +145 -0
  4. package/agents/debugger.md +83 -0
  5. package/agents/doc-generator.md +67 -0
  6. package/agents/docs-reference.md +86 -0
  7. package/agents/migrator.md +84 -0
  8. package/agents/process-consultant.md +97 -0
  9. package/agents/refactorer.md +75 -0
  10. package/agents/sx-configurator.md +67 -0
  11. package/commands/changelog.md +66 -0
  12. package/commands/diagnose.md +67 -0
  13. package/commands/docs.md +81 -0
  14. package/commands/document.md +67 -0
  15. package/commands/explain.md +60 -0
  16. package/commands/generate.md +111 -0
  17. package/commands/migrate.md +81 -0
  18. package/commands/process.md +111 -0
  19. package/commands/refactor.md +65 -0
  20. package/commands/review.md +60 -0
  21. package/commands/sxgen.md +98 -0
  22. package/commands/test.md +103 -0
  23. package/dist/index.d.ts +2 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +143 -0
  26. package/dist/index.js.map +1 -0
  27. package/package.json +30 -0
  28. package/skills/advpl-code-generation/SKILL.md +163 -0
  29. package/skills/advpl-code-generation/patterns-fwformbrowse.md +485 -0
  30. package/skills/advpl-code-generation/patterns-jobs.md +519 -0
  31. package/skills/advpl-code-generation/patterns-mvc.md +765 -0
  32. package/skills/advpl-code-generation/patterns-pontos-entrada.md +708 -0
  33. package/skills/advpl-code-generation/patterns-rest.md +974 -0
  34. package/skills/advpl-code-generation/patterns-soap.md +639 -0
  35. package/skills/advpl-code-generation/patterns-treport.md +481 -0
  36. package/skills/advpl-code-generation/patterns-workflow.md +779 -0
  37. package/skills/advpl-code-generation/templates-classes.md +1096 -0
  38. package/skills/advpl-code-review/SKILL.md +72 -0
  39. package/skills/advpl-code-review/rules-best-practices.md +444 -0
  40. package/skills/advpl-code-review/rules-modernization.md +290 -0
  41. package/skills/advpl-code-review/rules-performance.md +333 -0
  42. package/skills/advpl-code-review/rules-security.md +302 -0
  43. package/skills/advpl-debugging/SKILL.md +265 -0
  44. package/skills/advpl-debugging/common-errors.md +1124 -0
  45. package/skills/advpl-debugging/performance-tips.md +768 -0
  46. package/skills/advpl-refactoring/SKILL.md +139 -0
  47. package/skills/advpl-to-tlpp-migration/SKILL.md +293 -0
  48. package/skills/advpl-to-tlpp-migration/migration-checklist.md +122 -0
  49. package/skills/advpl-to-tlpp-migration/migration-rules.md +265 -0
  50. package/skills/changelog-patterns/SKILL.md +99 -0
  51. package/skills/code-explanation/SKILL.md +66 -0
  52. package/skills/documentation-patterns/SKILL.md +172 -0
  53. package/skills/embedded-sql/SKILL.md +379 -0
  54. package/skills/probat-testing/SKILL.md +226 -0
  55. package/skills/probat-testing/patterns-unit-tests.md +614 -0
  56. package/skills/protheus-business/SKILL.md +92 -0
  57. package/skills/protheus-business/modulo-compras.md +780 -0
  58. package/skills/protheus-business/modulo-contabilidade.md +874 -0
  59. package/skills/protheus-business/modulo-estoque.md +876 -0
  60. package/skills/protheus-business/modulo-faturamento.md +800 -0
  61. package/skills/protheus-business/modulo-financeiro.md +1015 -0
  62. package/skills/protheus-business/modulo-fiscal.md +749 -0
  63. package/skills/protheus-business/modulo-manutencao.md +848 -0
  64. package/skills/protheus-business/modulo-pcp.md +743 -0
  65. package/skills/protheus-reference/SKILL.md +119 -0
  66. package/skills/protheus-reference/native-functions.md +7029 -0
  67. package/skills/protheus-reference/rest-api-reference.md +1758 -0
  68. package/skills/protheus-reference/restricted-functions.md +265 -0
  69. package/skills/protheus-reference/sx-dictionary.md +854 -0
  70. package/skills/sx-configuration/SKILL.md +184 -0
@@ -0,0 +1,379 @@
1
+ ---
2
+ name: embedded-sql
3
+ description: Use when writing SQL queries in ADVPL/TLPP using BeginSQL/EndSQL blocks, %table%, %notDel%, %xfilial%, %exp% macros, or when choosing between Embedded SQL and TCQuery string concatenation
4
+ ---
5
+
6
+ # Embedded SQL in ADVPL/TLPP
7
+
8
+ ## Overview
9
+
10
+ Embedded SQL allows writing SQL queries directly in ADVPL/TLPP code using `BeginSQL ... EndSQL` blocks with special macro expressions. It replaces error-prone string concatenation (`cQuery += "SELECT..."`) with readable, type-safe, and maintainable SQL blocks.
11
+
12
+ ## When to Use
13
+
14
+ - Writing any SQL query in ADVPL/TLPP (prefer over string concatenation)
15
+ - Need to query with proper filial filtering, deletion handling, and table naming
16
+ - Building reports with complex SELECT, JOIN, GROUP BY
17
+ - Any situation where `TCQuery` with concatenated strings is currently used
18
+ - When readability and maintainability of SQL code matters
19
+
20
+ ## BeginSQL vs TCQuery (String Concatenation)
21
+
22
+ | Aspect | BeginSQL (Modern) | TCQuery + Strings (Legacy) |
23
+ |--------|-------------------|---------------------------|
24
+ | Readability | SQL written naturally | SQL buried in string concat |
25
+ | Table names | `%table:SE2%` automatic | `RetSqlName("SE2")` manual |
26
+ | Filial filter | `%xfilial:SE2%` automatic | `xFilial("SE2")` manual |
27
+ | Deletion filter | `%notDel%` automatic | `D_E_L_E_T_ = ' '` manual |
28
+ | Variable binding | `%exp:cVar%` | `"'" + cVar + "'"` (SQL injection risk) |
29
+ | Column types | `column X as Date` | `TCSetField` manual |
30
+ | Maintenance | Easy to read and modify | Hard to find errors in strings |
31
+ | SQL Injection | Protected via macros | Vulnerable if not careful |
32
+
33
+ **Always prefer BeginSQL for new code.**
34
+
35
+ ## Core Syntax
36
+
37
+ ```advpl
38
+ BeginSQL Alias cAlias
39
+ SELECT columns
40
+ FROM %table:ALIAS% ALIAS
41
+ WHERE ALIAS.%notDel%
42
+ AND ALIAS.FIELD_FILIAL = %xfilial:ALIAS%
43
+ AND ALIAS.FIELD = %exp:cVariable%
44
+ ORDER BY %Order:ALIAS%
45
+ EndSQL
46
+ ```
47
+
48
+ **Important:** The alias parameter can be a string literal or `GetNextAlias()`:
49
+
50
+ ```advpl
51
+ Local cAlias := GetNextAlias()
52
+
53
+ BeginSQL Alias cAlias
54
+ SELECT A1_COD, A1_LOJA, A1_NOME
55
+ FROM %table:SA1% SA1
56
+ WHERE SA1.%notDel%
57
+ AND SA1.A1_FILIAL = %xfilial:SA1%
58
+ EndSQL
59
+
60
+ DbSelectArea(cAlias)
61
+ While !Eof()
62
+ Conout(cAlias->A1_NOME)
63
+ DbSkip()
64
+ EndDo
65
+ DbCloseArea()
66
+ ```
67
+
68
+ ## Special Macro Expressions
69
+
70
+ ### %table:TABLE%
71
+
72
+ Resolves the physical table name with proper database schema/owner.
73
+
74
+ ```advpl
75
+ -- Input:
76
+ FROM %table:SA1% SA1
77
+
78
+ -- Expands to (example):
79
+ FROM SA1010 SA1
80
+ -- (where 010 = company code, depends on environment)
81
+ ```
82
+
83
+ ### %xfilial:TABLE%
84
+
85
+ Returns the current branch (filial) value for the table.
86
+
87
+ ```advpl
88
+ -- Input:
89
+ AND SA1.A1_FILIAL = %xfilial:SA1%
90
+
91
+ -- Expands to:
92
+ AND SA1.A1_FILIAL = '01'
93
+ -- (or '' if table is not branch-filtered)
94
+ ```
95
+
96
+ ### %notDel%
97
+
98
+ Filters out logically deleted records.
99
+
100
+ ```advpl
101
+ -- Input:
102
+ WHERE SA1.%notDel%
103
+
104
+ -- Expands to:
105
+ WHERE SA1.D_E_L_E_T_ <> '*'
106
+ ```
107
+
108
+ **Always include this.** Protheus uses logical deletion, not physical.
109
+
110
+ ### %exp:EXPRESSION%
111
+
112
+ Binds ADVPL variables, expressions, or function results into the SQL.
113
+
114
+ ```advpl
115
+ Local cCodCli := "000001"
116
+ Local cLoja := "01"
117
+ Local dDataIni := CtoD("01/01/2026")
118
+
119
+ BeginSQL Alias cAlias
120
+ SELECT E2_PREFIXO, E2_NUM, E2_VALOR, E2_EMISSAO
121
+ FROM %table:SE2% SE2
122
+ WHERE SE2.%notDel%
123
+ AND SE2.E2_FILIAL = %xfilial:SE2%
124
+ AND SE2.E2_FORNECE = %exp:cCodCli%
125
+ AND SE2.E2_LOJA = %exp:cLoja%
126
+ AND SE2.E2_EMISSAO >= %exp:DtoS(dDataIni)%
127
+ EndSQL
128
+ ```
129
+
130
+ **Note:** `%exp:%` handles quoting automatically for character fields. For dates, use `DtoS()` to convert.
131
+
132
+ ### %Order:TABLE%
133
+
134
+ Returns the primary key ordering for the table.
135
+
136
+ ```advpl
137
+ -- Input:
138
+ ORDER BY %Order:SE2%
139
+
140
+ -- Expands to the primary key columns of SE2
141
+ ```
142
+
143
+ ## Column Type Declaration
144
+
145
+ Declare result column types to avoid manual `TCSetField` calls:
146
+
147
+ ```advpl
148
+ BeginSQL Alias cAlias
149
+ column E2_EMISSAO as Date
150
+ column E2_VENCTO as Date
151
+ column E2_VALOR as Numeric(16,2)
152
+
153
+ SELECT E2_PREFIXO, E2_NUM, E2_EMISSAO, E2_VENCTO, E2_VALOR
154
+ FROM %table:SE2% SE2
155
+ WHERE SE2.%notDel%
156
+ AND SE2.E2_FILIAL = %xfilial:SE2%
157
+ EndSQL
158
+
159
+ // Columns are automatically typed - no TCSetField needed
160
+ DbSelectArea(cAlias)
161
+ While !Eof()
162
+ Local dEmissao := (cAlias)->E2_EMISSAO // Already Date type
163
+ Local nValor := (cAlias)->E2_VALOR // Already Numeric
164
+ DbSkip()
165
+ EndDo
166
+ ```
167
+
168
+ **Without `column` declaration**, all fields return as Character and need manual conversion.
169
+
170
+ ## JOIN Patterns
171
+
172
+ ### INNER JOIN
173
+
174
+ ```advpl
175
+ BeginSQL Alias cAlias
176
+ SELECT SE2.E2_PREFIXO, SE2.E2_NUM, SE2.E2_VALOR,
177
+ SA2.A2_NOME, SA2.A2_CGC
178
+ FROM %table:SE2% SE2
179
+ INNER JOIN %table:SA2% SA2
180
+ ON SE2.E2_FORNECE = SA2.A2_COD
181
+ AND SE2.E2_LOJA = SA2.A2_LOJA
182
+ AND SA2.A2_FILIAL = %xfilial:SA2%
183
+ AND SA2.%notDel%
184
+ WHERE SE2.%notDel%
185
+ AND SE2.E2_FILIAL = %xfilial:SE2%
186
+ AND SE2.E2_EMISSAO >= %exp:DtoS(dDataIni)%
187
+ ORDER BY SE2.E2_EMISSAO
188
+ EndSQL
189
+ ```
190
+
191
+ ### LEFT JOIN
192
+
193
+ ```advpl
194
+ BeginSQL Alias cAlias
195
+ SELECT SC5.C5_NUM, SC5.C5_CLIENTE, SC5.C5_LOJACLI,
196
+ SA1.A1_NOME
197
+ FROM %table:SC5% SC5
198
+ LEFT JOIN %table:SA1% SA1
199
+ ON SC5.C5_CLIENTE = SA1.A1_COD
200
+ AND SC5.C5_LOJACLI = SA1.A1_LOJA
201
+ AND SA1.A1_FILIAL = %xfilial:SA1%
202
+ AND SA1.%notDel%
203
+ WHERE SC5.%notDel%
204
+ AND SC5.C5_FILIAL = %xfilial:SC5%
205
+ EndSQL
206
+ ```
207
+
208
+ **Best practice:** Always use explicit JOIN syntax, not comma-separated FROM.
209
+
210
+ ## Aggregation Patterns
211
+
212
+ ### SUM / COUNT / AVG
213
+
214
+ ```advpl
215
+ BeginSQL Alias cAlias
216
+ column TOTAL as Numeric(16,2)
217
+ column QTD as Numeric(10,0)
218
+
219
+ SELECT SE2.E2_FORNECE, SE2.E2_LOJA,
220
+ SUM(SE2.E2_VALOR) AS TOTAL,
221
+ COUNT(*) AS QTD
222
+ FROM %table:SE2% SE2
223
+ WHERE SE2.%notDel%
224
+ AND SE2.E2_FILIAL = %xfilial:SE2%
225
+ AND SE2.E2_EMISSAO BETWEEN %exp:DtoS(dDataIni)% AND %exp:DtoS(dDataFim)%
226
+ GROUP BY SE2.E2_FORNECE, SE2.E2_LOJA
227
+ HAVING SUM(SE2.E2_VALOR) > 0
228
+ ORDER BY TOTAL DESC
229
+ EndSQL
230
+ ```
231
+
232
+ ### Subquery
233
+
234
+ ```advpl
235
+ BeginSQL Alias cAlias
236
+ SELECT SA1.A1_COD, SA1.A1_NOME
237
+ FROM %table:SA1% SA1
238
+ WHERE SA1.%notDel%
239
+ AND SA1.A1_FILIAL = %xfilial:SA1%
240
+ AND SA1.A1_COD IN (
241
+ SELECT SC5.C5_CLIENTE
242
+ FROM %table:SC5% SC5
243
+ WHERE SC5.%notDel%
244
+ AND SC5.C5_FILIAL = %xfilial:SC5%
245
+ AND SC5.C5_EMISSAO >= %exp:DtoS(dDataIni)%
246
+ )
247
+ EndSQL
248
+ ```
249
+
250
+ ## Complete Pattern: Query with Cursor Iteration
251
+
252
+ ```advpl
253
+ #Include "TOTVS.CH"
254
+ #Include "TopConn.ch"
255
+
256
+ User Function QueryEx()
257
+ Local cAlias := GetNextAlias()
258
+ Local aArea := GetArea()
259
+ Local aResult := {}
260
+ Local dDataIni := Date() - 30
261
+
262
+ BeginSQL Alias cAlias
263
+ column E2_EMISSAO as Date
264
+ column E2_VENCTO as Date
265
+ column E2_VALOR as Numeric(16,2)
266
+ column SALDO as Numeric(16,2)
267
+
268
+ SELECT SE2.E2_PREFIXO, SE2.E2_NUM, SE2.E2_PARCELA,
269
+ SE2.E2_FORNECE, SE2.E2_LOJA,
270
+ SE2.E2_EMISSAO, SE2.E2_VENCTO,
271
+ SE2.E2_VALOR,
272
+ (SE2.E2_VALOR - SE2.E2_PAGO) AS SALDO,
273
+ SA2.A2_NOME
274
+ FROM %table:SE2% SE2
275
+ INNER JOIN %table:SA2% SA2
276
+ ON SE2.E2_FORNECE = SA2.A2_COD
277
+ AND SE2.E2_LOJA = SA2.A2_LOJA
278
+ AND SA2.A2_FILIAL = %xfilial:SA2%
279
+ AND SA2.%notDel%
280
+ WHERE SE2.%notDel%
281
+ AND SE2.E2_FILIAL = %xfilial:SE2%
282
+ AND SE2.E2_EMISSAO >= %exp:DtoS(dDataIni)%
283
+ AND (SE2.E2_VALOR - SE2.E2_PAGO) > 0
284
+ ORDER BY SE2.E2_VENCTO
285
+ EndSQL
286
+
287
+ DbSelectArea(cAlias)
288
+ While !(cAlias)->(Eof())
289
+ aAdd(aResult, {;
290
+ (cAlias)->E2_PREFIXO,;
291
+ (cAlias)->E2_NUM,;
292
+ (cAlias)->E2_VALOR,;
293
+ (cAlias)->SALDO,;
294
+ (cAlias)->A2_NOME;
295
+ })
296
+ (cAlias)->(DbSkip())
297
+ EndDo
298
+ (cAlias)->(DbCloseArea())
299
+
300
+ RestArea(aArea)
301
+ Return aResult
302
+ ```
303
+
304
+ ## Restrictions and Gotchas
305
+
306
+ | Rule | Why |
307
+ |------|-----|
308
+ | Do NOT start a line with `*` inside BeginSQL | ADVPL pre-compiler treats `*` at line start as comment |
309
+ | Always include `%notDel%` | Protheus uses logical deletion, never rely on physical |
310
+ | Always include `%xfilial%` or filial filter | Multi-branch environments will return wrong data |
311
+ | Use `GetNextAlias()` for the alias | Avoids alias name conflicts with open work areas |
312
+ | Always `DbCloseArea()` after done | Prevents work area leaks (limited number of aliases) |
313
+ | Do NOT use `SELECT *` | Specify columns explicitly for performance and clarity |
314
+ | Date values must use `DtoS()` | Database stores dates as YYYYMMDD strings |
315
+ | `column` declarations go BEFORE the SELECT | They define result types, not query columns |
316
+
317
+ ## DML Operations
318
+
319
+ For INSERT, UPDATE, DELETE use `TCSqlExec` instead of BeginSQL:
320
+
321
+ ```advpl
322
+ // INSERT
323
+ Local cSql := "INSERT INTO " + RetSqlName("ZZ1") + " "
324
+ cSql += "(ZZ1_FILIAL, ZZ1_CODIGO, ZZ1_DESCRI, D_E_L_E_T_, R_E_C_N_O_) "
325
+ cSql += "VALUES ('" + xFilial("ZZ1") + "', '001', 'Teste', ' ', " + cValToChar(GetSxeNum("ZZ1","ZZ1_CODIGO")) + ")"
326
+ nRet := TCSqlExec(cSql)
327
+
328
+ // UPDATE
329
+ cSql := "UPDATE " + RetSqlName("ZZ1") + " SET "
330
+ cSql += "ZZ1_DESCRI = 'Novo Valor' "
331
+ cSql += "WHERE ZZ1_FILIAL = '" + xFilial("ZZ1") + "' "
332
+ cSql += "AND ZZ1_CODIGO = '001' "
333
+ cSql += "AND D_E_L_E_T_ = ' '"
334
+ nRet := TCSqlExec(cSql)
335
+
336
+ If nRet < 0
337
+ Conout("SQL Error: " + TCSqlError())
338
+ EndIf
339
+ ```
340
+
341
+ **BeginSQL is for SELECT only.** Use `TCSqlExec` for DML (INSERT/UPDATE/DELETE).
342
+
343
+ ## Performance Tips
344
+
345
+ 1. **Use `column` declarations** to avoid post-query type conversions
346
+ 2. **Use JOINs** instead of subqueries when possible
347
+ 3. **Filter early** with WHERE, not after reading all rows in ADVPL
348
+ 4. **Use NOLOCK** hint for read-only queries (SQL Server): `FROM %table:SA1% SA1 WITH(NOLOCK)`
349
+ 5. **Use TOP N** when you only need limited results
350
+ 6. **Close aliases** immediately after use to free work areas
351
+ 7. **Avoid loops with BeginSQL inside** - build one query that returns all needed data
352
+
353
+ ## Migration from TCQuery to BeginSQL
354
+
355
+ **Before (string concatenation):**
356
+ ```advpl
357
+ Local cQuery := ""
358
+ cQuery += "SELECT A1_COD, A1_NOME "
359
+ cQuery += "FROM " + RetSqlName("SA1") + " SA1 "
360
+ cQuery += "WHERE SA1.D_E_L_E_T_ = ' ' "
361
+ cQuery += "AND A1_FILIAL = '" + xFilial("SA1") + "' "
362
+ cQuery += "AND A1_TIPO = '" + cTipo + "'"
363
+ TCQuery cQuery New Alias "QRY_CLI"
364
+ ```
365
+
366
+ **After (Embedded SQL):**
367
+ ```advpl
368
+ Local cAlias := GetNextAlias()
369
+
370
+ BeginSQL Alias cAlias
371
+ SELECT A1_COD, A1_NOME
372
+ FROM %table:SA1% SA1
373
+ WHERE SA1.%notDel%
374
+ AND SA1.A1_FILIAL = %xfilial:SA1%
375
+ AND SA1.A1_TIPO = %exp:cTipo%
376
+ EndSQL
377
+ ```
378
+
379
+ Benefits: No manual `RetSqlName`, no `D_E_L_E_T_`, no `xFilial()`, no string quoting, no SQL injection risk.
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: probat-testing
3
+ description: Use when generating ProBat unit tests for TLPP classes and functions on TOTVS Protheus
4
+ ---
5
+
6
+ # ProBat Testing
7
+
8
+ ## Overview
9
+
10
+ ProBat is the native unit testing engine of tlppCore for creating and executing tests in TLPP programs on TOTVS Protheus. The name comes from Latin and means "proof/tests". It supports unit, functional, and integration tests, code coverage analysis, TDD workflows, and CI/CD integration.
11
+
12
+ > **ProBat only works with TLPP.** Tests for ADVPL sources must also be written in TLPP. If the target code is `.prw`, the test file is still `.tlpp`.
13
+
14
+ ## Required Include
15
+
16
+ Every test file must include the ProBat header:
17
+
18
+ ```tlpp
19
+ #include "tlpp-probat.th"
20
+ ```
21
+
22
+ If using tlppCore features (JsonObject, REST, etc.), also include:
23
+
24
+ ```tlpp
25
+ #include "tlpp-core.th"
26
+ #include "tlpp-probat.th"
27
+ ```
28
+
29
+ ## Namespace Convention
30
+
31
+ Test namespaces follow the pattern `test.<module>` or `custom.tests.<module>`:
32
+
33
+ ```tlpp
34
+ namespace test.financeiro
35
+ namespace custom.tests.faturamento
36
+ ```
37
+
38
+ ## Annotations
39
+
40
+ All annotations below are confirmed in the official TOTVS `tlpp-probat-samples` repository.
41
+
42
+ ### @TestFixture
43
+
44
+ Marks a function or class as a test fixture. This is the **only** way ProBat discovers test code.
45
+
46
+ ```tlpp
47
+ // Minimal
48
+ @TestFixture()
49
+
50
+ // With properties
51
+ @TestFixture(owner='team_name')
52
+ @TestFixture(owner='team_name', target='source_file.tlpp')
53
+ @TestFixture(owner='team_name', suite='suite_name')
54
+ @TestFixture(priority=1)
55
+ @TestFixture(runWithAll=.F.) // Exclude from "run all" mode
56
+ @TestFixture(rwa=.F.) // Short alias for runWithAll
57
+ ```
58
+
59
+ | Property | Type | Description |
60
+ |----------|------|-------------|
61
+ | `owner` | Character | Team or author identifier |
62
+ | `target` | Character | Source file being tested (for traceability) |
63
+ | `suite` | Character | Suite name (for grouped execution) |
64
+ | `priority` | Numeric | Execution order (lower = first) |
65
+ | `runWithAll` / `rwa` | Logical | If `.F.`, test is excluded from "run all" mode |
66
+
67
+ ### @Test
68
+
69
+ Marks a method in a `@TestFixture` class as a test to execute. The description parameter is **mandatory**.
70
+
71
+ ```tlpp
72
+ @Test('description of what is being tested')
73
+ public method myTestMethod()
74
+ ```
75
+
76
+ > If a class has `@TestFixture` but no methods with `@Test`, the result will be SKIPPED.
77
+
78
+ ### @OneTimeSetUp (runs once before all tests)
79
+
80
+ Marks a method to run **once** before all `@Test` methods in the class. Multiple `@OneTimeSetUp` methods are allowed.
81
+
82
+ ```tlpp
83
+ @OneTimeSetUp()
84
+ public method initDatabase()
85
+ ```
86
+
87
+ ### @Setup (runs before each test)
88
+
89
+ Marks a method to run **before each** `@Test` method.
90
+
91
+ ```tlpp
92
+ @Setup()
93
+ public method resetState()
94
+ ```
95
+
96
+ ### @TearDown (runs after each test)
97
+
98
+ Marks a method to run **after each** `@Test` method.
99
+
100
+ ```tlpp
101
+ @TearDown()
102
+ public method cleanupState()
103
+ ```
104
+
105
+ ### @OneTimeTearDown (runs once after all tests)
106
+
107
+ Marks a method to run **once** after all `@Test` methods in the class.
108
+
109
+ ```tlpp
110
+ @OneTimeTearDown()
111
+ public method closeConnections()
112
+ ```
113
+
114
+ ### @Skip
115
+
116
+ Skips an entire test fixture (function or class) or a specific method. Supports conditional filters.
117
+
118
+ ```tlpp
119
+ // Skip unconditionally
120
+ @Skip()
121
+
122
+ // Skip with filters
123
+ @Skip(system="windows")
124
+ @Skip(appServerName="HARPIA")
125
+ @Skip(tlppVersion=">= 01.02.10")
126
+ @Skip(system="windows", appServerName="HARPIA", tlppVersion=">= 01.02.10")
127
+ ```
128
+
129
+ ### @ErrorLog
130
+
131
+ Marks a test that is expected to produce an error. The test passes if the specified error occurs.
132
+
133
+ ```tlpp
134
+ @Test('test that expects an error')
135
+ @ErrorLog('type mismatch')
136
+ public method testExpectedError()
137
+ ```
138
+
139
+ ### SKIPASSERT (inline command)
140
+
141
+ Skips the next assertion only. Useful for conditional skipping inside a test.
142
+
143
+ ```tlpp
144
+ SKIPASSERT
145
+ assertError('this assert will be skipped')
146
+
147
+ // With filters
148
+ SKIPASSERT TLPPVERSION ">= 01.02.10"
149
+ SKIPASSERT SYSTEM "windows"
150
+ SKIPASSERT APPSERVERNAME "HARPIA"
151
+ SKIPASSERT SYSTEM "windows" APPSERVERNAME "HARPIA" TLPPVERSION ">= 01.02.10"
152
+ ```
153
+
154
+ ## Assertion Functions
155
+
156
+ All assertions belong to the `tlpp.probat` namespace. They can be called either by using the namespace directive or with the full qualified name.
157
+
158
+ ```tlpp
159
+ // Option 1: using namespace
160
+ using namespace tlpp.probat
161
+ assertEquals(xValue, xExpected, 'description')
162
+
163
+ // Option 2: full qualified name
164
+ tlpp.probat.assertEquals(xValue, xExpected, 'description')
165
+ ```
166
+
167
+ ### Complete Assertion Reference
168
+
169
+ | Function | Signature | Description |
170
+ |----------|-----------|-------------|
171
+ | `assertEquals` | `(xValue, xExpected [, cDesc])` | Values must be equal |
172
+ | `assertNotEquals` | `(xValue, xExpected [, cDesc])` | Values must be different |
173
+ | `assertTrue` | `(lValue [, cDesc])` | Value must be `.T.` |
174
+ | `assertFalse` | `(lValue [, cDesc])` | Value must be `.F.` |
175
+ | `assertGreater` | `(xValue, xCompare [, cDesc])` | Value must be greater than expected |
176
+ | `assertGreaterOrEqual` | `(xValue, xCompare [, cDesc])` | Value must be greater than or equal to expected |
177
+ | `assertLess` | `(xValue, xCompare [, cDesc])` | Value must be less than expected |
178
+ | `assertLessOrEqual` | `(xValue, xCompare [, cDesc])` | Value must be less than or equal to expected |
179
+ | `assertNil` | `(xValue [, cDesc])` | Value must be NIL |
180
+ | `assertVector` | `(aValue, aExpected [, cDesc])` | Arrays must be equal |
181
+ | `assertJson` | `(xValue, xExpected [, cDesc])` | JSON objects/strings must be equal |
182
+ | `assertIsRegExFull` | `(cValue, cPattern [, cDesc])` | String must fully match regex |
183
+ | `assertIsRegExPartial` | `(cValue, cPattern [, cDesc])` | String must partially match regex |
184
+ | `assertIsContained` | `(cValue, cSearch [, cDesc])` | String must contain the search value |
185
+ | `assertOK` | `(cDesc)` | Registers a positive result (always passes) |
186
+ | `assertError` | `(cDesc)` | Registers a negative result (always fails) |
187
+ | `assertWarning` | `(cDesc)` | Registers a warning (no pass/fail impact) |
188
+
189
+ ## Execution Lifecycle (Class-based)
190
+
191
+ ```
192
+ Constructor (new)
193
+ |
194
+ +-- @OneTimeSetUp methods (once)
195
+ |
196
+ +-- @Setup methods (before each test)
197
+ | |
198
+ | +-- @Test method
199
+ | |
200
+ | +-- @TearDown methods (after each test)
201
+ |
202
+ +-- @Setup methods (before each test)
203
+ | |
204
+ | +-- @Test method
205
+ | |
206
+ | +-- @TearDown methods (after each test)
207
+ |
208
+ +-- @OneTimeTearDown methods (once)
209
+ ```
210
+
211
+ ## Test Execution
212
+
213
+ ### Via REST API (programmatic)
214
+
215
+ Tests can be discovered and executed using ProBat's REST API or through the main runner. See the official `tlpp-probat-samples` repository for script-based execution examples.
216
+
217
+ ### Via VSCode Extension
218
+
219
+ TOTVS provides a VSCode extension for ProBat that integrates test discovery and execution directly into the editor.
220
+
221
+ ## References
222
+
223
+ - `patterns-unit-tests.md` - Templates and patterns for writing ProBat tests
224
+ - [Official samples](https://github.com/totvs/tlpp-probat-samples) - TOTVS official ProBat examples repository
225
+ - [TDN ProBat](https://tdn.totvs.com/display/tec/PROBAT) - Official documentation on TOTVS Developer Network
226
+ - [TDN Asserts](https://tdn.totvs.com/display/tec/d+-+Asserts) - Assertion functions reference