@suffolklitlab/alkiln 5.13.2 → 5.13.3-feat-random
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/.github/workflows/github_server.yml +7 -7
- package/.github/workflows/playground.yml +2 -1
- package/CHANGELOG.md +32 -1
- package/action.yml +89 -29
- package/action_for_github_server/action.yml +73 -29
- package/docs/4_environments.md +243 -0
- package/docs/artifacts_folder_name.md +135 -0
- package/docs/constrained_random_tests.md +254 -0
- package/docs/decisions/006_which_log_level_to_pick_2024_09_5.md +70 -0
- package/docs/decisions/007_getting_and_storing_logs.md +179 -0
- package/docs/decisions/move_runtime_config_to_sources_folder.md +5 -0
- package/docs/decisions/reports_and_logs_2024_08_11.md +161 -0
- package/docs/kinds_of_errors.md +26 -0
- package/docs/prioritize_constrained_random_tests.md +60 -0
- package/lib/docassemble/docassemble_api_interface.js +77 -51
- package/lib/docassemble/server_install.js +32 -9
- package/lib/docassemble/setup.js +41 -24
- package/lib/docassemble/takedown.js +33 -10
- package/lib/globals.js +10 -0
- package/lib/run_cucumber.js +155 -50
- package/lib/scope.js +101 -77
- package/lib/setup/TestGenerator.js +1745 -0
- package/lib/setup/artifacts.js +20 -0
- package/lib/setup/generate.js +42 -0
- package/lib/setup/get_Gherkin_AST.js +26 -0
- package/lib/setup/validate.js +89 -0
- package/lib/steps.js +176 -51
- package/lib/utils/Log.js +1132 -0
- package/lib/utils/_alkiln-constrained_random_tests_old.js +1727 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_error_stacks.js +16 -0
- package/lib/utils/reports.js +95 -28
- package/lib/utils/session_vars.js +102 -73
- package/lib/utils/set_sources_paths.js +98 -0
- package/lib/utils/time.js +44 -0
- package/package.json +16 -8
- package/tests/log_codes/check_codes.sh +19 -14
- package/tests/log_codes/log_code_expected_instances.txt +69 -9
- package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +155 -0
- package/tests/unit_tests/constrained_random_answers/invalid.test.js +233 -0
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +905 -0
- package/tests/unit_tests/constrained_random_answers/valid.test.js +521 -0
- package/tests/unit_tests/example_of_chai_test_lifecycle.js +78 -0
- package/tests/unit_tests/getAllFields.test.js +5 -0
- package/tests/unit_tests/getMatchingRows.test.js +5 -0
- package/tests/unit_tests/log.test.js +557 -0
- package/tests/unit_tests/normalizeTable.test.js +5 -0
- package/lib/utils/log.js +0 -96
- /package/docs/{expected_inernal_results.md → expected_internal_results.md} +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Designing for the complexity of the 4 environments in which ALKiln can run
|
|
2
|
+
|
|
3
|
+
## Context and scope
|
|
4
|
+
|
|
5
|
+
Authors have different testing needs. For example, sometimes authors need to run individual tests more quickly as they are developing code and sometimes authors need to run a whole test suite on a weekly schedule to make sure their online forms keep working over time. To run individual tests more quickly, an author can run the tests directly on their development server. To run scheduled tests, an author can run tests on GitHub. Those are very different environments.
|
|
6
|
+
|
|
7
|
+
Internal ALKiln developers need to run tests in more environments as well.
|
|
8
|
+
|
|
9
|
+
To help with all these, ALKiln can work in 4 different environments. We have to design ALKiln's architecture with that in mind.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Help authors to test in the environments that best help them develop.
|
|
14
|
+
- Help internal ALKiln developers develop the framework comfortably enough.
|
|
15
|
+
- Keep openings where authors can interact directly with the systems we're using so they can customize behavior that we can't account for.
|
|
16
|
+
|
|
17
|
+
## Non/anti-goals
|
|
18
|
+
|
|
19
|
+
- Be an intermediary between the author and all the possible customization they need for their tests.
|
|
20
|
+
|
|
21
|
+
## The actual design
|
|
22
|
+
|
|
23
|
+
This design involves:
|
|
24
|
+
|
|
25
|
+
- The 4 environments, the ALKiln flow for each, and the advantages each environment has.
|
|
26
|
+
- The 4 separate command line commands.
|
|
27
|
+
- Passing information between different runtime processes.
|
|
28
|
+
- The 2 composite action scripts.
|
|
29
|
+
|
|
30
|
+
<!-- Maybe the flows below should each be shown in the relevant individual sections for each environment. -->
|
|
31
|
+
|
|
32
|
+
The flow for each environment is different.
|
|
33
|
+
|
|
34
|
+
The temporary GitHub docasseble server action:
|
|
35
|
+
|
|
36
|
+
1. Author workflow file > `./action_for_github/action.yml` > author workflow file > `./action.yml` > `alkiln-server-install` > `alkiln-run` > author workflow file
|
|
37
|
+
|
|
38
|
+
<!-- Not sure how to represent this relationship. Trying to show what processes have access to what parts of the process. I'm not sure showing how `./action.yml` threads in and out is actually useful. I'm trying to show that the author's workflow has access in between `./action_for_github/action.yml` and `./action.yml` where they can do things so when someone reads about it later, they can understand.
|
|
39
|
+
|
|
40
|
+
Author workflow
|
|
41
|
+
`./action_for_github/action.yml`
|
|
42
|
+
Author workflow
|
|
43
|
+
`./action.yml`
|
|
44
|
+
`alkiln-server-install`
|
|
45
|
+
`./action.yml`
|
|
46
|
+
`alkiln-run`
|
|
47
|
+
`./action.yml`
|
|
48
|
+
Author workflow
|
|
49
|
+
|
|
50
|
+
Author workflow
|
|
51
|
+
`./action_for_github/action.yml`
|
|
52
|
+
Author workflow
|
|
53
|
+
`./action.yml`
|
|
54
|
+
`alkiln-server-install`
|
|
55
|
+
`alkiln-run`
|
|
56
|
+
Author workflow
|
|
57
|
+
-->
|
|
58
|
+
|
|
59
|
+
A GitHub action that installs tests in an author's account on their own docasseble server:
|
|
60
|
+
|
|
61
|
+
1. Author workflow file > `./action.yml` > `alkiln-setup` > `alkiln-run` > `alkiln-takedown` > author workflow file
|
|
62
|
+
|
|
63
|
+
<!-- Not sure how to represent this relationship
|
|
64
|
+
Author workflow
|
|
65
|
+
`./action.yml`
|
|
66
|
+
`alkiln-setup`
|
|
67
|
+
`alkiln-run`
|
|
68
|
+
`alkiln-takedown`
|
|
69
|
+
Author workflow
|
|
70
|
+
-->
|
|
71
|
+
|
|
72
|
+
ALKilnInThePlayground:
|
|
73
|
+
|
|
74
|
+
1. `alkiln-run`
|
|
75
|
+
|
|
76
|
+
A local developer might customize these steps in various ways in their console. If they are using the default workflow, this is what it looks like functionally when they develop a feature:
|
|
77
|
+
|
|
78
|
+
1. Command line > `alkiln-setup` once
|
|
79
|
+
2. Command line > `alkiln-run` many times
|
|
80
|
+
3. Command line > `alkiln-takedown` once
|
|
81
|
+
|
|
82
|
+
### 4 environments we need to account for
|
|
83
|
+
|
|
84
|
+
#### 2 GitHub options
|
|
85
|
+
|
|
86
|
+
Authors or admins use GitHub workflows to run our GitHub composite actions if they:
|
|
87
|
+
|
|
88
|
+
- Want to run tests every time an author commits code.
|
|
89
|
+
- Want to run tests based on other triggers, like a schedule or manually triggering the workflow.
|
|
90
|
+
- They want to require that all tests pass before they merge a branch.
|
|
91
|
+
- Use other CI pipelines.
|
|
92
|
+
|
|
93
|
+
**Run on a temporary GitHub server**
|
|
94
|
+
|
|
95
|
+
1. Author: commits code to GitHub.
|
|
96
|
+
2. ALKiln: Creates a docassemble server, installs the docassemble package globally on the server, run the test suite once, and destroys the server.
|
|
97
|
+
|
|
98
|
+
Authors use this if they:
|
|
99
|
+
|
|
100
|
+
- Want less flake.
|
|
101
|
+
- Want to test with latest version of da.
|
|
102
|
+
- Don't mind extra setup time each test suite takes.
|
|
103
|
+
- Know how to use bash to work with a Docker container (if needed).
|
|
104
|
+
|
|
105
|
+
**Run from GitHub, but on their own server**
|
|
106
|
+
|
|
107
|
+
1. Author: commits code to GitHub.
|
|
108
|
+
2. ALKiln: Installs the docassemble package in the author's account on their own docassemble server, runs the test suite once, and deletes the docassemble package.
|
|
109
|
+
|
|
110
|
+
Authors use this if they:
|
|
111
|
+
|
|
112
|
+
- Want to Use data from SS3 and other remote databases.
|
|
113
|
+
- Want to test with their own version of docassemble.
|
|
114
|
+
- Don't want to have to be so careful with all their dependencies (like having to put all dependencies on pypi) because they have installed them on their server themselves.
|
|
115
|
+
|
|
116
|
+
#### Running directly on docassemble server
|
|
117
|
+
|
|
118
|
+
A docassemble server doesn't just serve forms to end users. It also lets authors develop the code of their forms[^1]. That is why it is useful to install and use ALKiln on an author's docassemble server. They do this by using the AlKilnInThePlayground package.
|
|
119
|
+
|
|
120
|
+
An author installs AlKilnInThePlayground package on their server. They run the online form that package has. That lets them install ALKiln on their server. The package can tell who the user is and offers the user a choice of which of their Playground development Projects to test, and they run the tests right their on their server.
|
|
121
|
+
|
|
122
|
+
1. Author: Installs the package they want to test into their account on the docassemble 1. server, edits their package, triggers the test run.
|
|
123
|
+
2. ALKiln: Runs tests.
|
|
124
|
+
3. Author: Edits, tests, and repeats. Finally, the author deletes the package in their account on the docassemble server.
|
|
125
|
+
|
|
126
|
+
Authors use this if they:
|
|
127
|
+
|
|
128
|
+
- Want to run tests faster than the GitHub test flow allows (a faster iteration cycle).
|
|
129
|
+
- Want to avoid server reloads (in the case of a package with python modules).
|
|
130
|
+
- Feel uncomfortable with failures on GitHub - they can get tests passing before pushing to GitHub.
|
|
131
|
+
- Have the right version of nodejs on their docassemble server, which is based on what Docker image they have installed.
|
|
132
|
+
|
|
133
|
+
#### Command line
|
|
134
|
+
|
|
135
|
+
<!-- Do we mention that internal devs don't always use the bins? They use the package.json scripts? Seems like an unnecessary complication. -->
|
|
136
|
+
|
|
137
|
+
1. Internal developer with ALKiln's help or with their own command line arguments: Installs the package on a specified docassemble server (local or remote).
|
|
138
|
+
2. Internal developer with ALKiln's help: runs the tests as many times as they need while they develop the framework code.
|
|
139
|
+
3. Internal developer with ALKiln's help or with their own command line arguments: deletes the package from the docassemble server.
|
|
140
|
+
|
|
141
|
+
Internal developers do this if they:
|
|
142
|
+
|
|
143
|
+
- Have to edit ALKiln's own code.
|
|
144
|
+
- Want to run tests faster than the GitHub test flow allows (a faster iteration cycle).
|
|
145
|
+
- Want to run tests on a local server.
|
|
146
|
+
- See ALKiln's behavior live and with more detail.
|
|
147
|
+
|
|
148
|
+
### 4 node command line commands
|
|
149
|
+
|
|
150
|
+
`package.json` has 4 command-line commands. They are broken up by the different needs of the 4 different environments - installing packages on a server, saving temporary server information, running the core framework code[^2], deleting packages on a server. As this document describes. Some environments need only one of the commands.
|
|
151
|
+
|
|
152
|
+
Read the API section for more details.
|
|
153
|
+
|
|
154
|
+
Looking only at the command line commands does miss a crucial 1st part of the temporary GitHub docassemble server workflow - creating the docassemble server. That is done completely through `./action_for_github/action.yml`. See the section on GitHub composite actions for details.
|
|
155
|
+
|
|
156
|
+
### Passing information between different processes.
|
|
157
|
+
|
|
158
|
+
ALKiln needs to pass information between its different processes to do things like go to the right interview urls and store artifacts (like reports, debug logs, and screenshots) in the right folder.
|
|
159
|
+
|
|
160
|
+
**`runtime_config.json`**
|
|
161
|
+
|
|
162
|
+
Since different runtime processes can't pass information between them any other way, we create and modify `runtime_config.json` as each process runs to store information that other processes need. Different environments need to save different information or to save information differently.
|
|
163
|
+
|
|
164
|
+
For example, `alkiln-setup` needs to create a Project on an author's Playground[^1] to store the code of the online forms and show the forms online. Later, `alkiln-run` needs to know how to find the URL for that specific Project on the server so it can interact with the forms.
|
|
165
|
+
|
|
166
|
+
The name of the artifacts folder is an example of using `runtime_config.json` and how the complexity of these 4 environments invades ALKiln's core code. See the doc about the artifacts folder name.
|
|
167
|
+
|
|
168
|
+
<!-- Do we need to list the specific possible values here? These aren't recorded anywhere else, so it's worth considering, but it doesn't feel like the right place. This is incomplete. Holding off until we decide where it goes.
|
|
169
|
+
Everything uses these:
|
|
170
|
+
|
|
171
|
+
- `artifacts_path`
|
|
172
|
+
|
|
173
|
+
Temp GitHub docassemble server install uses these:
|
|
174
|
+
|
|
175
|
+
`da_install_method` - `server` or `playground`
|
|
176
|
+
`da_repo_folder_name` - For example, EvictionDefense in the docassemble-EvictionDefense repo. (see session_vars.js)
|
|
177
|
+
|
|
178
|
+
Installation in an author's account on their own docassemble server and command line uses these:
|
|
179
|
+
|
|
180
|
+
Does ALKilnInThePlayground also use the below?
|
|
181
|
+
|
|
182
|
+
- `da_project_name` - The name of the docassemble project. Helps get the url of the interview. In local internal development, this lets developers keep using the same Project over and over so they don't need to make a new Project, and upload the package code, every time they run a test.
|
|
183
|
+
-->
|
|
184
|
+
|
|
185
|
+
**Env vars**
|
|
186
|
+
|
|
187
|
+
ALKiln and test authors use environment variables to pass information to different processes. GitHub workflows and actions store them in their `.yml` files. ALKilnInThePlayground uses the server's configuration file. Local developers use their `.env` file. This is also the way authors and internal developers pass sensitive information to ALKiln. On GitHub, authors store sensitive information in GitHub secrets and then use them as environment variables.
|
|
188
|
+
|
|
189
|
+
### 2 GitHub composite actions
|
|
190
|
+
|
|
191
|
+
Authors use our GitHub composite actions in their GitHub workflow files.
|
|
192
|
+
|
|
193
|
+
- `./action_for_github/action.yml` - Creates the temporary GitHub docassemble server. This action is a crucial part of the flow for temporary GitHub docassemble servers. The fact that it is a separate action gives authors the information and environment they need to change and customize the temporary server themselves in more ways than we can account for. For example, authors might need to make user accounts so they can test what a logged-in user sees. Right now, it cannot help them access remote databases like S3.
|
|
194
|
+
- `./action.yml` - Installs packages on servers in the ways we've described, runs the tests, deletes packages on servers, and gives authors information from the test run, like reports and screenshots.
|
|
195
|
+
|
|
196
|
+
The rest of the details and API are in the files themselves and in the author-facing documentation about GitHub workflow files at https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#workflows.
|
|
197
|
+
|
|
198
|
+
## APIs
|
|
199
|
+
|
|
200
|
+
### Command-line API
|
|
201
|
+
|
|
202
|
+
ALKiln's `package.json` offers 4 command-line commands:
|
|
203
|
+
|
|
204
|
+
1. `alkiln-server-install` - Stores `runtime_config.json` variables of a temporary GitHub docassemble server for the core framework code[^2].
|
|
205
|
+
2. `alkiln-setup` - Installs the docassemble package on the author's account and stores `runtime_config.json` variables for core framework code.
|
|
206
|
+
3. `alkiln-run` - Wraps and then runs the framework's core code to run the tests.
|
|
207
|
+
4. `alkiln-takedown` - Deletes the package there.
|
|
208
|
+
|
|
209
|
+
### GitHub composite actions API
|
|
210
|
+
|
|
211
|
+
The general description of the composite actions is in the composite action section. The details of composite action API is in the author-facing documentation about GitHub workflow files at https://assemblyline.suffolklitlab.org/docs/alkiln/writing/#workflows.
|
|
212
|
+
|
|
213
|
+
## Degree of constraint
|
|
214
|
+
|
|
215
|
+
This design is working within systems that limit how we can do what we need to do.
|
|
216
|
+
|
|
217
|
+
- GitHub's action and workflow API and environment. For example, interacting with the temporary GitHub docassemble server vs. interacting with an author's server. Also, the ways in which we need to store and use environment variables.
|
|
218
|
+
- docassemble's API and environment. It wasn't designed to be automated and used this way. For example, it stores package files very differently than the way GitHub arranges them and very differently depending on where we install the package, it has different memory constraints for installing the packages globally vs. in an individual developer's account, etc.
|
|
219
|
+
- cucumberjs's inability to pass non-primitives to core framework code[^2].
|
|
220
|
+
- 3rd party database storage systems, like S3, are hard to integrate into GitHub temporary server tests. We have excluded this feature at the moment. Authors would need to write their own workflow or composite action to access remote databases.
|
|
221
|
+
|
|
222
|
+
## Alternatives considered
|
|
223
|
+
|
|
224
|
+
Alternative: Get rid of our support for some of these environments. The temporary GitHub docassemble server environment hasn't been used by a lot of authors. Other than that, the other environments have gotten a lot of use. Even though authors have not yet used the temporary GitHub environment much, they say they want that functionality. The trouble there seems to be complexity with initial set up.
|
|
225
|
+
|
|
226
|
+
Alternative: Instead of dealing with separate runtimes, have one command and script that wraps all these currently separate processes in one runtime and handles which ones to trigger under what circumstances. If we included `./action_for_github/action.yml` in that command, we would prevent authors from customizing the temporary GitHub docassemble server. That would not be acceptable. To avoid closing off access to server customization, we can only partially integrate these different processes. Also, GitHub actions in general will still have to deal with some core business sometimes. For example, they have to create the artifacts folder name so they can use it later to upload the artifacts. This combined runtime also feels more coupled/entangled and therefore harder to maintain. The need for more command line arguments could make local development more complicated. It is still worth considering.
|
|
227
|
+
|
|
228
|
+
<!-- Discuss: How to articulate the entanglement with more specifics and examples. -->
|
|
229
|
+
|
|
230
|
+
Alternative script names: `install_package`, `save_server`, `delete_package`.
|
|
231
|
+
|
|
232
|
+
## Cross-cutting concerns
|
|
233
|
+
|
|
234
|
+
_Security, privacy, observability, etc._
|
|
235
|
+
|
|
236
|
+
- Some tests need to use sensitive information. For example, some tests need a logged-in user and thus ALKiln needs the login info for a user. Different environments store this information differently and there is documentation about how to use sensitive information securely and privately.
|
|
237
|
+
- Core ALKiln code[^2] ends up needing to handle some of the complexity of the 4 environments. Example - the `Log` class `path` takes an optional initial argument to set the artifacts path at the beginning of that runtime process. Other parts of the process have to assume that this path already exists. We have tried to abstract environment complexity out to `run_cucumber.js`, the script that wraps the core code, but there is still logic that the core code handles.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
[^1]: A docassemble server can have multiple purposes. One is to contain the packages for online forms which end users then use. Those packages are stored in a special place on the server and admins configure the server to give end users a list of those forms. Another purpose of the docassemble server is to be a development environment in which to create the forms. An author/developer of the forms has a developer account on the server. That account can store multiple packages and versions of packages. It also has an IDE that authors can use to work on those packages. They can push those files to GitHub from that IDE. They can also pull those packages from GitHub using that IDE.
|
|
242
|
+
|
|
243
|
+
[^2]: **Core framework code/core code:** the code that implements the Steps in the `.feature` test files.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Creating and using the name of the artifacts folder
|
|
2
|
+
|
|
3
|
+
## Context and scope
|
|
4
|
+
|
|
5
|
+
The artifacts folder is where an author can see the results of their tests, like the reports, screenshots, and downloaded documents.
|
|
6
|
+
|
|
7
|
+
The name of this folder has a specific format to make it as short as possible while it still stays recognizable to users and that it is unique. To help keep it unique it includes a timestamp. (The human readable date/time is not as precise and also longer than the timestamp).
|
|
8
|
+
|
|
9
|
+
Because of the 4 environments ALKiln can run in (see the docs about the 4 environments), we have different ways to make sure we use one unique artifacts folder name for each test run.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
- Minimize code duplication
|
|
14
|
+
- In each of the 4 ALKiln environments, be able to track one unique folder name for each test suite run
|
|
15
|
+
|
|
16
|
+
## The actual design
|
|
17
|
+
|
|
18
|
+
Our 4 environments can be condensed into 3 environments for the purpose of managing the artifacts folder name:
|
|
19
|
+
|
|
20
|
+
- GitHub action (`./action.yml`)
|
|
21
|
+
- ALKilnInThePlayground
|
|
22
|
+
- The command line for internal development
|
|
23
|
+
|
|
24
|
+
The GitHub action creates an artifact folder name and sends it to various scripts that use an instance of the `Log` class to create the folder and save the name. In other environments, an instance of the `Log` class both creates the name, creates the folder, and saves the name. The instance of the `Log` saves the name in `runtime_config.json`. That instance of `Log` and other ALKiln code uses that name to create and store files in that artifacts folder.
|
|
25
|
+
|
|
26
|
+
The artifact folder name generation code is in 2 places - the GitHub action and ALKiln's core code[^1]. In future, we may reduce that to just one location - the core code.
|
|
27
|
+
|
|
28
|
+
<!-- More complex description that I don't think we need now, but want to double check:
|
|
29
|
+
The scripts that GitHub action uses need to store its output in the same artifacts folder. The GitHub action therefore builds the name of the artifact folder and passes that around to the different scripts. The scripts pass the folder name to the `Log` class every time, which saves the folder name in `runtime_config.json`. After that, the core code uses that `json` value to put other artifacts in the same folder. We also have folder-naming code in the core code which we use for other purposes. The GitHub action duplicates that folder-naming code, which is unfortunate.
|
|
30
|
+
|
|
31
|
+
At the same time, tests that run in ALKilnInThePlayground don't have GitHub to manage the artifact folder name. It only uses the `run_cucumber` script, not any of the others...
|
|
32
|
+
|
|
33
|
+
Adding to that confusion, when people (like internal ALKiln developers) run tests from their command line, they do use the `setup` script once at first, but then run test suites repeatedly...
|
|
34
|
+
|
|
35
|
+
Neither of the last 2 can depend on the `runtime_config.json` folder name that `setup` creates. If they did, all their multiple test runs would save to that first folder. Since both those environments use `run_cucumber.js` alone repeatedly, and since we don't want to duplicate folder-naming behavior between even more scripts, we decided that those non-GitHub environments should be able to let `Log` create a folder name from scratch every time. The `Log` constructor has to be able to accept a `path` argument with a folder name and also to accept an undefined `path` argument.
|
|
36
|
+
-->
|
|
37
|
+
|
|
38
|
+
A future goal is to remove the need to store the name in the runtime configuration file to reduce that complexity a bit. These flows are up for discussion.
|
|
39
|
+
|
|
40
|
+
Below are more detailed descriptions of the different paths the folder name takes in the different environments. Where the folder name is created, gets passed, gets saved, and gets used. Note that there's a little glossing over the details. For example, everything saves to `runtime_config.json`, but only the core code[^1] ever uses the value there.
|
|
41
|
+
|
|
42
|
+
The diagrams highlight `Log` to help visualize a common point in all the flows.
|
|
43
|
+
|
|
44
|
+
### GitHub action
|
|
45
|
+
|
|
46
|
+
1. GitHub action creates a new folder name, passes that name to `setup`, which passes that name to `Log`, which creates the artifacts folder and saves the name in `runtime_config.json`. The setup `Log` uses the name to store logs in the folder. The setup uses its `Log`.
|
|
47
|
+
2. GitHub action uses the same folder name, passes that same name to `run_cucumber`, which passes that same name to `Log`. The `runtime_cucumber` process uses that `Log`. The `run_cucumber` process also triggers the core code, which uses the folder name in `runtime_config.json` to create its own `Log`. The core code also uses that name to store other artifact files in the folder.
|
|
48
|
+
3. GitHub action creates the folder name, passes that same name to `takedown`, which passes that same name when instantiating its `Log`. The takedown `Log` uses the name to store logs in the folder.
|
|
49
|
+
|
|
50
|
+
```mermaid
|
|
51
|
+
%% "pass name too" has 2 o's at the end because mermaid makes one of them disappear
|
|
52
|
+
|
|
53
|
+
flowchart LR
|
|
54
|
+
action[action part 1] --create/pass name too--> setup
|
|
55
|
+
setup["setup"] --pass name too--> log1["#96;Log#96;"]:::log
|
|
56
|
+
log1 --store name in/ignore--> runtime1["runtime config file"]
|
|
57
|
+
log1 --create with name--> folder1[folder]
|
|
58
|
+
|
|
59
|
+
action2[action part 2] --pass name too--> run["run"]
|
|
60
|
+
run --pass name too--> log2["#96;Log#96;"]:::log
|
|
61
|
+
log2 --store name in/ignore--> runtime2["runtime config file"]
|
|
62
|
+
log2 --use with name--> folder2[folder]
|
|
63
|
+
run --> core["core code"]
|
|
64
|
+
core --pass name too--> log3["#96;Log#96;"]:::log
|
|
65
|
+
core --use with name--> folder2
|
|
66
|
+
core --get name from--> runtime2
|
|
67
|
+
log3 --use with name--> folder2
|
|
68
|
+
log3 --store name in/ignore--> runtime2
|
|
69
|
+
|
|
70
|
+
action3[action part 3] --pass name too--> takedown["takedown"]
|
|
71
|
+
takedown --pass name too--> log4["#96;Log#96;"]:::log
|
|
72
|
+
log4 --store name in/ignore--> runtime3["runtime config file"]
|
|
73
|
+
log4 --create with name--> folder5[folder]
|
|
74
|
+
|
|
75
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### ALKilnInThePlayground
|
|
79
|
+
|
|
80
|
+
1. ALKilnInThePlayground triggers `run_cucumber` without a folder name which triggers `Log` without a folder name. `Log` creates a new folder name and saves it in `runtime_config.json`. It also creates the artifacts folder. The `run_cucumber` process also triggers the core code, which uses the folder name in `runtime_config.json` to create its own `Log`. The core code also uses that name to store other artifact files in the folder.
|
|
81
|
+
|
|
82
|
+
```mermaid
|
|
83
|
+
flowchart LR
|
|
84
|
+
ALKilnInThePlayground --> run["run"]
|
|
85
|
+
run --> log2["#96;Log#96;"]:::log
|
|
86
|
+
log2 --store name in/ignore--> runtime2["runtime config file"]
|
|
87
|
+
log2 --create with name--> folder2[folder]
|
|
88
|
+
run --> core["core code"]
|
|
89
|
+
core --> log3["#96;Log#96;"]:::log
|
|
90
|
+
core --use with name--> folder2
|
|
91
|
+
core --get name from--> runtime2
|
|
92
|
+
log3 --use with name--> folder2
|
|
93
|
+
log3 --store name in/ignore--> runtime2
|
|
94
|
+
|
|
95
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Command line
|
|
99
|
+
|
|
100
|
+
1. The developer uses the command line to trigger setup without a folder name which triggers `Log` without a folder name. `Log` creates a name and creates the artifacts folder, then stores files in that folder.
|
|
101
|
+
2. The developer uses the command line to trigger `run_cucumber` without a folder name which triggers `Log` without a folder name. `Log` creates and saves a new folder name in `runtime_config.json`, and creates the artifacts folder. The `run_cucumber` process also triggers the core code, which uses the folder name in `runtime_config.json` to create its own `Log`. The core code also uses that name to store other artifact files in the folder.
|
|
102
|
+
3. The developer uses the command line to trigger `takedown` without a folder name which triggers `Log` without a folder name. `Log` creates a name and the artifacts folder, then stores files in that folder.
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
flowchart LR
|
|
106
|
+
cmd1[command line] --> setup
|
|
107
|
+
setup["setup"] --> log1["#96;Log#96;"]:::log
|
|
108
|
+
log1 --store name in/ignore--> runtime1["runtime config file"]
|
|
109
|
+
log1 --create with/use with name--> folder1[folder]
|
|
110
|
+
|
|
111
|
+
cmd2[command line] --> run["run"]
|
|
112
|
+
run --> log2["#96;Log#96;"]:::log
|
|
113
|
+
log2 --store name in/ignore--> runtime2["runtime config file"]
|
|
114
|
+
log2 --create with name--> folder2[folder]
|
|
115
|
+
run --> core["core code"]
|
|
116
|
+
core --> log3["#96;Log#96;"]:::log
|
|
117
|
+
core --use with name--> folder2
|
|
118
|
+
core --get name from--> runtime2
|
|
119
|
+
log3 --use with name--> folder2
|
|
120
|
+
log3 --store name in/ignore--> runtime2
|
|
121
|
+
|
|
122
|
+
cmd3[command line] --> takedown["takedown"]
|
|
123
|
+
takedown --> log4["#96;Log#96;"]:::log
|
|
124
|
+
log4 --store name in/ignore--> runtime3["runtime config file"]
|
|
125
|
+
log4 --create with name--> folder5[folder]
|
|
126
|
+
|
|
127
|
+
classDef log fill:#d6fd88,stroke:#000,stroke-width:2px,color:#000
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Alternatives considered
|
|
131
|
+
|
|
132
|
+
- One script for creating the artifacts folder name: Make a script that saves the folder name to the ALKiln `runtime_config.json`. Then all the other code, including instances of `Log`, would always use `runtime_config.json`. We would want `package.json` scripts for the command line environment that would include getting the name of the folder. That potentially complicates the code and process for internal development. We will see how our current design behaves first.
|
|
133
|
+
- Use `Log`'s path name instead of the one in `runtime_config.json`. We thought we would want to save in previous artifacts folders, but it now seems potentially unnecessary. This seems the most viable option for the future, but it is unclear how we can integrate `Log` with all the other files. I wouldn't consider PDFs and screenshots to be logs.
|
|
134
|
+
|
|
135
|
+
[^1]: **Core framework code/core code:** the code that implements the Steps in the `.feature` test files.
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# Constrained random answers tests architecture and decisions
|
|
2
|
+
|
|
3
|
+
(stub)
|
|
4
|
+
|
|
5
|
+
Authors are eager to try random answers tests. Development time for testing is hard to find and almost no test suite is exhaustive. They hope to trigger errors they may not have thought to look for.
|
|
6
|
+
|
|
7
|
+
## Pros and cons of random tests in general
|
|
8
|
+
|
|
9
|
+
Pros:
|
|
10
|
+
|
|
11
|
+
- Less work for the test author.
|
|
12
|
+
- Can catch errors authors didn't think to test.
|
|
13
|
+
|
|
14
|
+
Cons:
|
|
15
|
+
|
|
16
|
+
- The tests are less deliberate and thought-through.
|
|
17
|
+
- The tests may give authors a false sense of security.
|
|
18
|
+
|
|
19
|
+
## Why set a maximum number of tests per Scenario?
|
|
20
|
+
|
|
21
|
+
Because someone might intend to write 10 and add an extra 0 accidentally. These tests can take a long time to run.
|
|
22
|
+
|
|
23
|
+
## Concerns with the current flow
|
|
24
|
+
|
|
25
|
+
- The usual process for troubleshooting failing GitHub tests is to re-run the tests. Authors may not realize that they get all new randomized tests with each run _and_ rerun. If the random tests fail during the first run, but pass during the second run, they may mistakenly believe the first failure was a fluke.
|
|
26
|
+
- How easy/hard will it be for ALKilnInThePlayground users to come back to previous runs to get their old generated tests? We will have to see how users find it.
|
|
27
|
+
|
|
28
|
+
## Fast failure
|
|
29
|
+
|
|
30
|
+
Based on author feedback, constrained random answers test generation should fail early and fast and avoid being too permissive, though some mistakes can get away with warnings.
|
|
31
|
+
|
|
32
|
+
## The generating process
|
|
33
|
+
|
|
34
|
+
The main test generation logic copies the existing generator Scenario strings and transforms them. This doc gives an example of the transformation. This doesn't show the order of what happens in the code. It just shows how ALKiln thinks about the transformation.
|
|
35
|
+
|
|
36
|
+
A generator file can only have generator Scenarios in it, though it can have any number of generator Scenarios. For each generator file ALKiln gets, ALKiln makes one file with the right generated tests.
|
|
37
|
+
|
|
38
|
+
### Part 1 - ALKiln gets this generator file
|
|
39
|
+
|
|
40
|
+
```gherkin
|
|
41
|
+
@go_places_maybe
|
|
42
|
+
Feature: I go places
|
|
43
|
+
|
|
44
|
+
I go to places like the store, the park, the zoo, etc.
|
|
45
|
+
|
|
46
|
+
Scenario: I might go to the store
|
|
47
|
+
Given I start the interview at "store.yml"
|
|
48
|
+
And I make 2 constrained random tests that get to any of ["kickout", "success"] with:
|
|
49
|
+
| var | possible_values |
|
|
50
|
+
| user_goes | True;; False |
|
|
51
|
+
Then I wait .2 seconds
|
|
52
|
+
|
|
53
|
+
# Check with Jaime - is the wording correct?
|
|
54
|
+
Scenario: I visit the zoo
|
|
55
|
+
Given I start the interview at "zoo.yml"
|
|
56
|
+
And I generate 5 constrained random tests that get to any of ["end", "restart"] with:
|
|
57
|
+
| var | possible_values |
|
|
58
|
+
#...etc
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
By the end of the process there should be 1 file with 7 unique Scenarios.
|
|
62
|
+
|
|
63
|
+
### Part 2 - ALKiln breaks the file up into sections
|
|
64
|
+
|
|
65
|
+
Section 1 - the top of the file till the first generator Scenario:
|
|
66
|
+
|
|
67
|
+
```gherkin
|
|
68
|
+
@go_places_maybe
|
|
69
|
+
Feature: I go places
|
|
70
|
+
|
|
71
|
+
I go to places like the store, the park, the zoo, etc.
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Section 2 - the text of generator Scenario 1 until its constraints Step:
|
|
76
|
+
|
|
77
|
+
```gherkin
|
|
78
|
+
@store @obvious
|
|
79
|
+
Scenario: I might go to the store
|
|
80
|
+
Given I start the interview at "store.yml"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Section 3 - the constrained random answers Step of generator Scenario 1:
|
|
84
|
+
|
|
85
|
+
```gherkin
|
|
86
|
+
And I generate 2 random tests that get to any of ["kickout", "success"] when I pick from these constraints:
|
|
87
|
+
| var | possible_values |
|
|
88
|
+
| user_goes | True;; False |
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Section 4 - the text of generator Scenario 1 after the constraints Step until the next generator Scenario:
|
|
92
|
+
```gherkin
|
|
93
|
+
Then I wait .2 seconds
|
|
94
|
+
|
|
95
|
+
# Check with Jaime - is the wording correct?
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Section 5 - top of generator Scenario 2 until its constraints Step:
|
|
99
|
+
|
|
100
|
+
```gherkin
|
|
101
|
+
Scenario: I visit the zoo
|
|
102
|
+
Given I sign in at ""
|
|
103
|
+
And I start the interview at "zoo.yml"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
TODO:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
And I generate 5 constrained random tests that get to any of ["end", "restart"] with:
|
|
110
|
+
| var | possible_values |
|
|
111
|
+
#...etc
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
And so on down the file.
|
|
115
|
+
|
|
116
|
+
### Part 3 - ALKiln creates multiple Scenarios with Story Table Steps
|
|
117
|
+
|
|
118
|
+
1st Story Table Step for generator Scenario 1:
|
|
119
|
+
|
|
120
|
+
```gherkin
|
|
121
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
122
|
+
| var | value |
|
|
123
|
+
| user_goes | False |
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
2nd Story Table Step for generator Scenario 1:
|
|
127
|
+
|
|
128
|
+
```gherkin
|
|
129
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
130
|
+
| var | value |
|
|
131
|
+
| user_goes | True |
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
1st Story Table Step for generator Scenario 2:
|
|
135
|
+
|
|
136
|
+
```gherkin
|
|
137
|
+
And I get to any of ["end", "restart"] with this data:
|
|
138
|
+
| var | value |
|
|
139
|
+
#...etc
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
And so on for the other 4 Story Table Steps for generator Scenario 2.
|
|
143
|
+
|
|
144
|
+
### Part 4 - Build whole Scenarios
|
|
145
|
+
|
|
146
|
+
Combine the Story Table Steps with the start and end of their corresponding Scenarios.
|
|
147
|
+
|
|
148
|
+
1st generated Scenario based on generator Scenario 1:
|
|
149
|
+
|
|
150
|
+
```gherkin
|
|
151
|
+
@store @obvious
|
|
152
|
+
Scenario: I might go to the store
|
|
153
|
+
Given I start the interview at "store.yml"
|
|
154
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
155
|
+
| var | value |
|
|
156
|
+
| user_goes | False |
|
|
157
|
+
Then I wait .2 seconds
|
|
158
|
+
|
|
159
|
+
# Check with Jaime - is the wording correct?
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
2nd generated Scenario based on generator Scenario 1:
|
|
163
|
+
|
|
164
|
+
```gherkin
|
|
165
|
+
@store @obvious
|
|
166
|
+
Scenario: I might go to the store
|
|
167
|
+
Given I start the interview at "store.yml"
|
|
168
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
169
|
+
| var | value |
|
|
170
|
+
| user_goes | True |
|
|
171
|
+
Then I wait .2 seconds
|
|
172
|
+
|
|
173
|
+
# Check with Jaime - is the wording correct?
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
1st generated Scenario based on generator Scenario 2:
|
|
177
|
+
|
|
178
|
+
```gherkin
|
|
179
|
+
Scenario: I visit the zoo
|
|
180
|
+
Given I start the interview at "zoo.yml"
|
|
181
|
+
And I get to any of ["end", "restart"] with this data:
|
|
182
|
+
| var | value |
|
|
183
|
+
#...etc
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Part 5 - Combine everything and add generation information
|
|
187
|
+
|
|
188
|
+
We want to make sure authors know these tests were generated by ALKiln and we want to make each Scenario unique. We need unique text that is visible during test output and we need unique tags authors can use to test individual tests.
|
|
189
|
+
|
|
190
|
+
```gherkin
|
|
191
|
+
@go_places_maybe @alkiln @generated @randomized @constraints
|
|
192
|
+
Feature: I go places with ALKiln randomization
|
|
193
|
+
|
|
194
|
+
I go to places like the store, the park, the zoo, etc.
|
|
195
|
+
|
|
196
|
+
@store @obvious @generator1_scenario1_of_2
|
|
197
|
+
Scenario: I might go to the store (1 of 2)
|
|
198
|
+
Given I start the interview at "store.yml"
|
|
199
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
200
|
+
| var | value |
|
|
201
|
+
| user_goes | False |
|
|
202
|
+
Then I wait .2 seconds
|
|
203
|
+
|
|
204
|
+
# Check with Jaime - is the wording correct?
|
|
205
|
+
@store @obvious @generator1_scenario2_of_2
|
|
206
|
+
Scenario: I might go to the store (2 of 2)
|
|
207
|
+
Given I start the interview at "store.yml"
|
|
208
|
+
And I get to any of ["kickout", "success"] with this data:
|
|
209
|
+
| var | value |
|
|
210
|
+
| user_goes | True |
|
|
211
|
+
Then I wait .2 seconds
|
|
212
|
+
|
|
213
|
+
# Check with Jaime - is the wording correct?
|
|
214
|
+
@generator2_scenario1_of_5
|
|
215
|
+
Scenario: I visit the zoo (1 of 5)
|
|
216
|
+
Given I start the interview at "zoo.yml"
|
|
217
|
+
And I get to any of ["end", "restart"] with this data:
|
|
218
|
+
| var | value |
|
|
219
|
+
#...etc
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
See unit tests for more examples.
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
@go_places_maybe
|
|
227
|
+
Feature: I go places
|
|
228
|
+
|
|
229
|
+
I go to places like the store, the park, the zoo, etc.
|
|
230
|
+
|
|
231
|
+
Scenario: I might go to the store
|
|
232
|
+
Given I will make 10 random constrained tests
|
|
233
|
+
Given I start the interview at "store.yml"
|
|
234
|
+
And constrained random tests get to any of ["kickout", "success"] with:
|
|
235
|
+
| var | possible_values |
|
|
236
|
+
| user_goes | True;; False |
|
|
237
|
+
Then I wait .2 seconds
|
|
238
|
+
And constrained random tests get to any of ["download"] with:
|
|
239
|
+
| var | possible_values |
|
|
240
|
+
| user_goes | True;; False |
|
|
241
|
+
And I see the phrase "Goodbye"
|
|
242
|
+
|
|
243
|
+
# Check with Jaime - is the wording correct?
|
|
244
|
+
Scenario: I visit the zoo
|
|
245
|
+
Given I start the interview at "zoo.yml"
|
|
246
|
+
And I generate 5 constrained random tests that get to any of ["end", "restart"] with:
|
|
247
|
+
| var | possible_values |
|
|
248
|
+
#...etc
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Why not just create a new AST?
|
|
252
|
+
|
|
253
|
+
The AST doesn't have a list of lines. For examples, Scenarios and comments are in separate lists. The AST also stores line numbers, so we'd have to keep updating line numbers for every object, which adds complexity. We've currently decided that the more structured model isn't worth that complexity.
|
|
254
|
+
|