teuton 3.2.1 → 3.4.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -20
  3. data/docs/commands/README.md +12 -10
  4. data/docs/commands/check.md +17 -15
  5. data/docs/commands/{readme.md → doc.md} +3 -5
  6. data/docs/commands/help.md +23 -13
  7. data/docs/devel/todo.md +2 -8
  8. data/docs/dsl/README.md +9 -8
  9. data/docs/dsl/event.md +172 -0
  10. data/docs/dsl/target.md +0 -14
  11. data/docs/install/t-node.md +10 -21
  12. data/docs/learn/01-cmd_new.md +1 -1
  13. data/docs/learn/02-target.md +52 -50
  14. data/docs/learn/03-remote_hosts.md +15 -16
  15. data/docs/learn/04-config.md +70 -69
  16. data/docs/learn/05-use.md +1 -1
  17. data/docs/learn/06-cmd_check.md +28 -26
  18. data/docs/learn/07-target_weight.md +7 -5
  19. data/docs/learn/08-unique_values.md +32 -30
  20. data/docs/learn/09-send.md +2 -2
  21. data/docs/learn/10-debug.md +38 -1
  22. data/docs/learn/11-export.md +1 -1
  23. data/docs/learn/12-preserve.md +4 -4
  24. data/docs/learn/13-feedback.md +12 -11
  25. data/docs/learn/14-moodle_id.md +3 -3
  26. data/docs/learn/15-readme.md +12 -12
  27. data/docs/learn/16-include.md +9 -9
  28. data/docs/learn/17-alias.md +2 -2
  29. data/docs/learn/18-log.md +46 -44
  30. data/docs/learn/19-read_vars.md +1 -1
  31. data/docs/learn/20-macros.md +1 -1
  32. data/docs/learn/21-exit_codes.md +1 -1
  33. data/docs/learn/22-result.md +1 -1
  34. data/docs/learn/23-test-code.md +14 -11
  35. data/docs/learn/24-test-sql.md +6 -6
  36. data/docs/learn/25-expect-result.md +1 -1
  37. data/docs/learn/26-expect_sequence.md +2 -2
  38. data/docs/learn/27-run_script.md +9 -6
  39. data/docs/learn/28-upload.md +3 -1
  40. data/docs/learn/31-event-stop.md +134 -0
  41. data/docs/learn/32-event-zero.md +127 -0
  42. data/docs/more.md +4 -1
  43. data/lib/teuton/case/{action.rb → action/action.rb} +18 -10
  44. data/lib/teuton/case/action/manager.rb +198 -0
  45. data/lib/teuton/case/case.rb +15 -13
  46. data/lib/teuton/case/close.rb +7 -7
  47. data/lib/teuton/case/dsl/all.rb +1 -0
  48. data/lib/teuton/case/dsl/event.rb +5 -0
  49. data/lib/teuton/case/dsl/expect.rb +6 -4
  50. data/lib/teuton/case/dsl/log.rb +10 -9
  51. data/lib/teuton/case/dsl/macro.rb +1 -1
  52. data/lib/teuton/case/dsl/run.rb +10 -15
  53. data/lib/teuton/case/dsl/target.rb +10 -3
  54. data/lib/teuton/case/play.rb +1 -1
  55. data/lib/teuton/case/result/ext_compare.rb +10 -10
  56. data/lib/teuton/case_manager/dsl.rb +6 -2
  57. data/lib/teuton/case_manager/ext/report.rb +0 -4
  58. data/lib/teuton/case_manager/show_resume_report.rb +29 -30
  59. data/lib/teuton/check/checker.rb +19 -8
  60. data/lib/teuton/check/dsl/all.rb +10 -16
  61. data/lib/teuton/check/dsl/event.rb +10 -0
  62. data/lib/teuton/check/dsl/expect.rb +21 -45
  63. data/lib/teuton/check/dsl/getset.rb +2 -2
  64. data/lib/teuton/check/dsl/run.rb +2 -2
  65. data/lib/teuton/check/dsl/target.rb +17 -0
  66. data/lib/teuton/check/show.rb +35 -41
  67. data/lib/teuton/cli/app.rb +16 -8
  68. data/lib/teuton/readme/dsl/all.rb +16 -2
  69. data/lib/teuton/readme/main.rb +2 -2
  70. data/lib/teuton/report/formatter/default/array.rb +15 -29
  71. data/lib/teuton/report/formatter/default/txt.rb +42 -30
  72. data/lib/teuton/report/formatter/resume/txt.rb +27 -26
  73. data/lib/teuton/report/formatter/resume/xml.rb +4 -4
  74. data/lib/teuton/report/report.rb +5 -36
  75. data/lib/teuton/utils/project.rb +4 -3
  76. data/lib/teuton/version.rb +1 -1
  77. metadata +14 -7
  78. data/docs/tutorial/es/nginx/README.md +0 -546
  79. data/lib/teuton/deprecated/utils.rb +0 -14
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # feedback
4
4
 
@@ -36,14 +36,15 @@ With `feedback: false` the fields are hidden: command, alterations, expected and
36
36
  Executing `teuton run examples/13-feedback`, we get this output:
37
37
 
38
38
  ```
39
- GROUPS
40
- - Hide feedback messages from output
41
- 01 (1.0/1.0)
42
- Description : Service SSH disabled
43
- Command : *********************
44
- Output : *********
45
- Duration : 0.011 (local)
46
- Alterations : **********************
47
- Expected : **************
48
- Result : ********
39
+ ☰ GROUPS
40
+ ▶ Hide feedback messages from output
41
+ ✔ 01 (1.0/1.0) ┌──
42
+ Description │ Service SSH disabled
43
+ Command │ *********************
44
+ Output │ *********
45
+ Duration │ 0.145 (local)
46
+ Alterations │ **********************
47
+ Expected │ **************
48
+ Result │ ********
49
+ └──
49
50
  ```
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Moodle
4
4
 
@@ -12,7 +12,7 @@ Add a new field called `tt_moodle_id` to each case in "config.yaml". Fill it wit
12
12
 
13
13
  Example:
14
14
 
15
- ```
15
+ ```yaml
16
16
  global:
17
17
  cases:
18
18
  - tt_members: Darth Vader
@@ -31,7 +31,7 @@ We use `tt_moodle_max_score` to customize Moodle max score. So when Teuton expor
31
31
 
32
32
  ## Example
33
33
 
34
- ```
34
+ ```yaml
35
35
  global:
36
36
  tt_moodle_max_score: 10
37
37
  cases:
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # readme
4
4
 
@@ -11,12 +11,12 @@ Take a look at our test definition section (Group):
11
11
  ```ruby
12
12
  group "Customize readme output" do
13
13
 
14
- readme "This is our README example."
15
- readme "And here we'll see how to use readme keyword"
14
+ doc "This is our README example."
15
+ doc "And here we'll see how to use readme keyword"
16
16
 
17
17
  target "Create user david."
18
- readme "Help: you can use 'useradd' command to create users."
19
- readme "Remember: Only root is permitted to create new users."
18
+ doc "Help: you can use 'useradd' command to create users."
19
+ doc "Remember: Only root is permitted to create new users."
20
20
 
21
21
  run "id david"
22
22
  expect "david"
@@ -26,30 +26,30 @@ end
26
26
 
27
27
  > In this example, localhost's OS must be GNU/Linux (any other compatible OS) because the command used is `id david`.
28
28
 
29
- There exists some `readme` instructions after `group` and `target` lines.
29
+ There exists some `doc` instructions after `group` and `target` lines.
30
30
 
31
31
  ## Execution
32
32
 
33
- To generate automatically a README file from previous test, execute this:
33
+ To generate automatically a Markdown file from previous test, execute this:
34
34
 
35
35
  ```
36
- $ teuton readme example/15-readme > example/15-readme/README.md
36
+ $ teuton doc example/15-doc > example/15-doc/README.md
37
37
  ```
38
38
 
39
39
  ## Result
40
40
 
41
- Content of `example/15-readme/README.md` file.
41
+ Content of `example/15-doc/README.md` file.
42
42
 
43
43
  ---
44
44
 
45
45
  ```
46
- Date : 2025-11-12 22:11:47 +0000
47
- Teuton : 2.10.7
46
+ Date : 2026-10-21 21:54:49 +0100
47
+ Teuton : 3.2.1
48
48
  ```
49
49
 
50
50
  # Test: 15-readme
51
51
 
52
- ## Customize readme output
52
+ ## Customize doc output
53
53
  This is our readme example.
54
54
  And here we'll see how to use readme keyword
55
55
 
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # include
4
4
 
@@ -58,12 +58,12 @@ Config files into `moreconfigfiles` folder:
58
58
 
59
59
  If we execute the test we will see that 3 cases are processed. Which are defined in the files `file01.yaml`, `02/file02.yaml` and `file03.yml`.
60
60
 
61
- ```
62
- CASE RESULTS
63
- +------+---------+-------+-------+
64
- | CASE | MEMBERS | GRADE | STATE |
65
- | 01 | file02 | 0.0 | ? |
66
- | 02 | file01 | 100.0 | ✔ |
67
- | 03 | file03 | 0.0 | ? |
68
- +------+---------+-------+-------+
61
+ ```
62
+ ☰ CASE RESULTS
63
+ ┌──────┬────────────┬───────┬───────┐
64
+ │ CASE │ MEMBERS │ GRADE │ STATE │
65
+ │ 01 │ student_02 │ 0.0 │ ? │
66
+ │ 02 │ student_01 │ 100.0 │ ✔ │
67
+ │ 03 │ student_03 │ 0.0 │ ? │
68
+ └──────┴────────────┴───────┴───────┘
69
69
  ```
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # alias
4
4
 
@@ -50,4 +50,4 @@ cases:
50
50
  username: obiwan
51
51
  ```
52
52
 
53
- Now our test will work correctly. Calling `get(:super)` will return the same value as doing `get(:superuser).
53
+ Now our test will work correctly. Calling `get(:super)` will return the same value as doing `get(:superuser)`.
data/docs/learn/18-log.md CHANGED
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # log
4
4
 
@@ -53,49 +53,51 @@ end
53
53
  Content of `var/18-log/case-01.txt` file.
54
54
 
55
55
  ```txt
56
- CONFIGURATION
57
- +----------------+-----------------------------------+
58
- | tt_config_path | examples/18-log/config.yaml |
59
- | tt_members | TOCHANGE |
60
- | tt_script_path | examples/18-log/start.rb |
61
- | tt_sequence | false |
62
- | tt_skip | false |
63
- | values1 | ["A1", "B1", "C1"] |
64
- | values2 | {:p1=>"A2", :p2=>"B2", :p3=>"C2"} |
65
- +----------------+-----------------------------------+
56
+ $ cat var/18-log/case-01.txt
57
+
58
+ CONFIGURATION
59
+ ┌────────────────┬───────────────────────────────────┐
60
+ │ tt_config_path │ examples/18-log/config.yaml │
61
+ │ tt_members │ TOCHANGE │
62
+ │ tt_script_path │ examples/18-log/start.rb │
63
+ │ tt_sequence │ false │
64
+ │ tt_skip │ false │
65
+ │ values1 │ ["A1", "B1", "C1"] │
66
+ │ values2 │ {:p1=>"A2", :p2=>"B2", :p3=>"C2"} │
67
+ └────────────────┴───────────────────────────────────┘
66
68
 
67
69
  LOGS
68
- [22:35:04] INFO: Using log messages.
69
- [22:35:04] INFO: uid=1000(david) gid=1000(david) grupos=1000(david),478(wheel),464(vboxusers),108(libvirt),107(qemu),462(vagrant),460(docker),452(ollama)
70
- [22:35:04] ERROR: Problem detected!
71
- [22:35:04] WARN!: This is a warning
72
- [22:35:04] INFO: Hi, there!
73
- [22:35:04] INFO: -------------
74
- [22:35:04] INFO: Array values : ["A1", "B1", "C1"]
75
- [22:35:04] INFO: Hash values : {:p1=>"A2", :p2=>"B2", :p3=>"C2"}
76
-
77
- GROUPS
78
- - Learning about log messages
79
- 01 (1.0/1.0)
80
- Description : Create user david
81
- Command : id david
82
- Output : uid=1000(david) gid=1000(david) grupos=1000(david),478(wheel),464(vboxusers),108(libvirt),107(qemu),462(vagrant),460(docker),452(ollama)
83
- Duration : 0.005 (local)
84
- Alterations : find(david) & count
85
- Expected : Greater than 0
86
- Result : 1
87
-
88
- RESULTS
89
- +--------------+---------------------------+
90
- | unique_fault | 0 |
91
- | case_id | 01 |
92
- | start_time | 2026-10-12 22:35:04 +0100 |
93
- | finish_time | 2026-10-12 22:35:04 +0100 |
94
- | duration | 0.004786563 |
95
- | max_weight | 1.0 |
96
- | good_weight | 1.0 |
97
- | fail_weight | 0 |
98
- | fail_counter | 0 |
99
- | grade | 100 |
100
- +--------------+---------------------------+
70
+ [22:00:21] INFO| Using log messages.
71
+ [22:00:21] INFO| uid=1000(david) gid=1000(david) grupos=1000(david),478(wheel),464(vboxusers),108(libvirt),107(qemu),462(vagrant),460(docker),452(ollama)
72
+ [22:00:21] ERROR| Problem detected!
73
+ [22:00:21] WARN| This is a warning
74
+ [22:00:21] INFO| Hi, there!
75
+ [22:00:21] INFO| -------------
76
+ [22:00:21] INFO| Array values : ["A1", "B1", "C1"]
77
+ [22:00:21] INFO| Hash values : {:p1=>"A2", :p2=>"B2", :p3=>"C2"}
78
+
79
+ GROUPS
80
+ ▶ Learning about log messages
81
+ ✔ 01 (1.0/1.0) ┌──
82
+ Description │ Create user david
83
+ Command │ id david
84
+ Output │ uid=1000(david) gid=1000(david) grupos=1000(david),478(wheel),464(vboxusers),108(libvirt),107(qemu),462(vagrant),460(docker),452(ollama)
85
+ Duration │ 0.005 (local)
86
+ Alterations │ find(david) & count
87
+ Expected │ Greater than 0
88
+ Result │ 1
89
+ └──
90
+ RESULTS
91
+ ┌──────────────┬───────────────────────────┐
92
+ │ unique_fault │ 0 │
93
+ │ case_id │ 01 │
94
+ │ start_time │ 2026-10-21 21:57:57 +0100 │
95
+ │ finish_time │ 2026-10-21 21:57:57 +0100 │
96
+ │ duration │ 0.005315073 │
97
+ │ max_weight │ 1.0 │
98
+ │ good_weight │ 1.0 │
99
+ │ fail_weight │ 0 │
100
+ │ fail_counter │ 0 │
101
+ │ grade │ 100 │
102
+ └──────────────┴───────────────────────────┘
101
103
  ```
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Read vars
4
4
 
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Macros
4
4
 
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Example: exit_codes
4
4
 
@@ -1,4 +1,4 @@
1
- [<< back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # result
4
4
 
@@ -1,4 +1,4 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Test code
4
4
 
@@ -43,22 +43,25 @@ Define config params:
43
43
  global:
44
44
  folder: examples/23-test-code/code
45
45
  cases:
46
- - tt_members: student_1
46
+ - tt_members: student_1_python
47
47
  filename: math_1.py
48
- - tt_members: student_2
48
+ - tt_members: student_2_python
49
49
  filename: math_2.py
50
+ - tt_members: student_3_ruby
51
+ filename: math_3.rb
50
52
  ```
51
53
 
52
54
  * Copy students files into `code` subfolder.
53
55
  * Now run Teuton test:
54
56
 
55
57
  ```
56
- $ teuton examples/23-test-code
57
-
58
- CASE RESULTS
59
- +------+-----------+-------+-------+
60
- | CASE | MEMBERS | GRADE | STATE |
61
- | 01 | student_1 | 100.0 | ✔ |
62
- | 02 | student_2 | 100.0 | ✔ |
63
- +------+-----------+-------+-------+
58
+ $ teuton examples/23-test-code
59
+
60
+ CASE RESULTS
61
+ ┌──────┬──────────────────┬───────┬───────┐
62
+ │ CASE │ MEMBERS │ GRADE │ STATE │
63
+ │ 01 │ student_1_python │ 100.0 │ ✔ │
64
+ │ 02 │ student_2_python │ 100.0 │ ✔ │
65
+ │ 03 │ student_3_ruby │ 100.0 │ ✔ │
66
+ └──────┴──────────────────┴───────┴───────┘
64
67
  ```
@@ -1,4 +1,4 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # Test SQL and database content
4
4
 
@@ -62,9 +62,9 @@ cases:
62
62
  ```
63
63
  $ teuton examples/24-test-sql
64
64
 
65
- CASE RESULTS
66
- +------+-----------+-------+-------+
67
- | CASE | MEMBERS | GRADE | STATE |
68
- | 01 | student_1 | 100.0 | ✔ |
69
- +------+-----------+-------+-------+
65
+ CASE RESULTS
66
+ ┌──────┬────────────────┬───────┬───────┐
67
+ │ CASE │ MEMBERS │ GRADE │ STATE │
68
+ │ 01 │ student_1_name │ 100.0 │ ✔ │
69
+ └──────┴────────────────┴───────┴───────┘
70
70
  ```
@@ -1,4 +1,4 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # expect vs result
4
4
 
@@ -1,4 +1,4 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # DSL: expect_sequence
4
4
 
@@ -8,7 +8,7 @@ Evaluate the occurrence of a certain sequence that takes place in different line
8
8
 
9
9
  ## Description
10
10
 
11
- In the classic target/run/expect, the expect statement works by locating lines from the output of the previous command that meet certain criteria.
11
+ In the classic "target/run/expect", the expect statement works by locating lines from the output of the previous command that meet certain criteria.
12
12
 
13
13
  We can even (using regular expressions) detect if there is any line in the output where a certain sequence appears.
14
14
 
@@ -1,4 +1,4 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # DSL: run_script
4
4
 
@@ -90,8 +90,11 @@ CONN ERRORS
90
90
  +------+---------+-------+------------------+
91
91
  ```
92
92
 
93
- Meaning of progress symbols:
94
- * `.`: check ok
95
- * `F`: check fail
96
- * `!`: connection fail
97
- * `u`: upload ok
93
+ **Meaning of progress symbols**
94
+
95
+ | Symbol | Description |
96
+ | ------ | --------------- |
97
+ | `.` | check ok |
98
+ | `F` | check fail |
99
+ | `!` | connection fail |
100
+ | `u` | upload ok |
@@ -1,9 +1,11 @@
1
- [<<back](README.md)
1
+ [<<back](../../README.md)
2
2
 
3
3
  # DSL: upload
4
4
 
5
5
  `upload` is a dsl instruction, whose purpose is to upload files to the remote host.
6
6
 
7
+ > **Recommendation**: Use this function as little as possible, to minimize any modifications, changes, or alterations to the remote device.
8
+
7
9
  ```ruby
8
10
  upload "FILENAME", to: :host1
9
11
  ```
@@ -0,0 +1,134 @@
1
+ [<<back](../../README.md)
2
+
3
+ # Event: stop
4
+
5
+ Sometimes it may be necessary to trigger an action when a specific condition occurs; for this, we can use events.
6
+
7
+ Events are defined with the following structure:
8
+
9
+ ```ruby
10
+ event on: OBJECT, when: CONDITION do
11
+ ACTION
12
+ end
13
+ ```
14
+
15
+ Where
16
+ * OBJECT can be `:last_target`
17
+ * CONDITION can be `:failed`
18
+ * And ACTION can be `stop`, `scores_to_zero`, or both.
19
+
20
+ ## Example
21
+
22
+ In this example, we have 4 targets, all of which will function correctly except for target 3.
23
+
24
+ ```ruby
25
+ group "Events: stop" do
26
+ target "Target 1: ok"
27
+ run "id root"
28
+ expect_ok
29
+
30
+ target "Target 2: ok"
31
+ run "id root"
32
+ expect_ok
33
+
34
+ target "Target 3: fail"
35
+ run "id vader"
36
+ expect_ok
37
+
38
+ event on: :last_target, when: :failed do
39
+ stop
40
+ end
41
+
42
+ target "Target 4: ok"
43
+ run "id root"
44
+ expect_ok
45
+ end
46
+ ```
47
+
48
+ An event is defined to trigger the `stop` action when the last target (target 3) fails.
49
+
50
+ The "stop" action halts the test, setting the number of targets yet to be evaluated to zero, while the rest retain their current assessment.
51
+
52
+ ## Report output
53
+
54
+ ```
55
+ $ teuton examples/31-event-stop
56
+
57
+ 🗹 CASE RESULTS
58
+ ┌──────┬──────────┬───────┬───────┐
59
+ │ CASE │ MEMBERS │ GRADE │ STATE │
60
+ │ 01 │ TOCHANGE │ 50.0 │ │
61
+ └──────┴──────────┴───────┴───────┘
62
+ ```
63
+
64
+ Process:
65
+ * target 1 is Ok.
66
+ * target 2 is Ok.
67
+ * target 3 fails so the `stop` action is triggered.
68
+ * target 4 is not evaluated because test was stopped.
69
+
70
+ ```
71
+ $ cat var/31-event-stop/case-01.txt
72
+
73
+ ⚒ CONFIGURATION
74
+ ┌────────────────┬────────────────────────────────────┐
75
+ │ tt_config_path │ examples/31-event-stop/config.yaml │
76
+ │ tt_members │ TOCHANGE │
77
+ │ tt_script_path │ examples/31-event-stop/start.rb │
78
+ │ tt_sequence │ false │
79
+ │ tt_skip │ false │
80
+ └────────────────┴────────────────────────────────────┘
81
+
82
+ 🛈 LOGS
83
+ [16:27:43] INFO| Event[0] triggered because target[3] failed
84
+ [16:27:43] INFO| Test STOPPED!
85
+
86
+ ☰ GROUPS
87
+ ▶ Events: stop
88
+ ✔ 01 (1.0/1.0) ┌──
89
+ Description │ Target 1: ok
90
+ Command │ id root
91
+ Output │ uid=0(root) gid=0(root) grupos=0(root)
92
+ Duration │ 0.002 (local)
93
+ Alterations │ Read exit code
94
+ Expected │ 0
95
+ Result │ 0
96
+ ✔ 02 (1.0/1.0) ├──
97
+ Description │ Target 2: ok
98
+ Command │ id root
99
+ Output │ uid=0(root) gid=0(root) grupos=0(root)
100
+ Duration │ 0.001 (local)
101
+ Alterations │ Read exit code
102
+ Expected │ 0
103
+ Result │ 0
104
+ ✖ 03 (0.0/1.0) ├──
105
+ Description │ Target 3: fail
106
+ Command │ id vader
107
+ Output │ id: «vader»: no existe ese usuario
108
+ Duration │ 0.002 (local)
109
+ Alterations │ Read exit code
110
+ Expected │ 0
111
+ Result │ 1
112
+ ✖ 04 (0.0/1.0) ├──
113
+ Description │ TEST STOPPED!
114
+ Command │
115
+ Output │
116
+ Duration │ ? (?)
117
+ Alterations │
118
+ Expected │
119
+ Result │
120
+ └──
121
+ 🗹 RESULTS
122
+ ┌──────────────┬───────────────────────────┐
123
+ │ unique_fault │ 0 │
124
+ │ case_id │ 01 │
125
+ │ start_time │ 2026-10-30 16:27:43 +0000 │
126
+ │ finish_time │ 2026-10-30 16:27:43 +0000 │
127
+ │ duration │ 0.006686742 │
128
+ │ max_weight │ 4.0 │
129
+ │ good_weight │ 2.0 │
130
+ │ fail_weight │ 2.0 │
131
+ │ fail_counter │ 2 │
132
+ │ grade │ 50 │
133
+ └──────────────┴───────────────────────────┘
134
+ ```