@suffolklitlab/alkiln 5.13.2 → 5.13.3-feat-random-2
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 +10 -8
- package/.github/workflows/playground.yml +5 -2
- package/.github/workflows/unit_tests.yml +1 -1
- package/CHANGELOG.md +32 -1
- package/_alkiln_meta/shift_logs.js +75 -0
- package/action.yml +89 -31
- package/action_for_github_server/action.yml +65 -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 +40 -25
- package/lib/docassemble/takedown.js +33 -10
- package/lib/globals.js +10 -0
- package/lib/run_cucumber.js +141 -69
- package/lib/scope.js +101 -77
- package/lib/setup/TestGenerator.js +1755 -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 +132 -54
- package/lib/utils/Log.js +1145 -0
- package/lib/utils/_alkiln-constrained_random_tests_old.js +1776 -0
- package/lib/utils/files.js +18 -11
- package/lib/utils/get_error_stacks.js +16 -0
- package/lib/utils/reports.js +100 -34
- package/lib/utils/session_vars.js +107 -73
- package/lib/utils/set_sources_paths.js +98 -0
- package/lib/utils/time.js +44 -0
- package/package.json +18 -7
- package/tests/log_codes/check_codes.sh +18 -13
- package/tests/log_codes/log_code_expected_instances.txt +91 -9
- package/tests/unit_tests/constrained_random_answers/invalid.fixtures.js +177 -0
- package/tests/unit_tests/constrained_random_answers/invalid.test.js +251 -0
- package/tests/unit_tests/constrained_random_answers/valid.fixtures.js +936 -0
- package/tests/unit_tests/constrained_random_answers/valid.test.js +541 -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,26 @@
|
|
|
1
|
+
# Kinds of Errors
|
|
2
|
+
|
|
3
|
+
An error, or even an `Error` instance, can mean a lot of different things in the context of this testing framework. Throwing errors for failing tests is what we are most familiar with, but it is just one kind of error.
|
|
4
|
+
|
|
5
|
+
## Interview `Error`s
|
|
6
|
+
|
|
7
|
+
1. **Interview bugs**: For example, the interview says ALKiln's answer is invalid. The author expected that answer to be valid. ALKiln saves an error message to the report and throws an error to tell cucumber to mark the test as failed. Currently, these logs errors come with the 🤕 icon. When all the tests are done, the test suite will show as failing in the console, the report, and the various logs. In future, ALKiln will use a method like `report.throw()` to save to the various log files with the `error` level and then throw the error to let cucumber know the test failed.
|
|
8
|
+
1. **Interview expected errors**: For example, the interview says ALKiln's answer is invalid. The author was testing their interview's field validation rules. The test passes.
|
|
9
|
+
|
|
10
|
+
## Test file `Error`s
|
|
11
|
+
|
|
12
|
+
1. **Test file syntax error**: For example, a row in a table in a test file has too many columns. cucumberjs throws an error and no tests are run. ALKiln should show the tests as failed. ALKiln is unable to get the output of that error into a log, but there should be an error in the console.
|
|
13
|
+
1. **Test Step typo**: For example, `And I ta p to contcontinue`. cucumberjs will cause this test to fail, giving an "undefined" Step as the reason. The test suite will show as failing in the console, the report, and the various logs.
|
|
14
|
+
1. **Test bugs**: For example, a test has old values that need to be updated. The test fails. This will look like an interview bug, as this document describes.
|
|
15
|
+
|
|
16
|
+
## ALKiln `Error`s
|
|
17
|
+
|
|
18
|
+
1. **ALKiln un-handled bugs**: For example, we have forgotten to define a variable and none of our tests has yet triggered that code. These are unexpected errors that force processes to abort or force individual tests to fail in uninformative ways. To an author that runs into that error, it looks like their test has failed, but the fault is ours and we need to troubleshoot it. We should make that clear.
|
|
19
|
+
1. **ALKiln silent bugs**: For example, a debug log has an object that causes an error when ALKiln tries to turn it into a string. ALKiln should record the failure somewhere, but should avoid throwing it. We can troubleshoot those if we see them and add new internal tests. See decisions docs on when to use which kind of log level.
|
|
20
|
+
1. **ALKiln expected errors**: For example, one promise won a `Promise.race()`, so ALKiln aborted the other promises. That causes those aborted promises to error. That is why ALKiln saves the "informational" Error to the debug files and it has a level of "info" or "debug".
|
|
21
|
+
|
|
22
|
+
## ALKiln's internal tests
|
|
23
|
+
|
|
24
|
+
1. **ALKiln internal success-handling test failures**: For example, the test fails to fill out radio buttons correctly.
|
|
25
|
+
1. **ALKiln internal failure-handling test successes**: For example, we test that the summary report shows a specific log code when an interview fails to load and the log code shows up correctly. The "test" fails - the interview fails to load - but *our* test passes - the right log code was in the summary report.
|
|
26
|
+
1. **ALKiln internal failure-handling test failures**: For example, we test that the report shows a specific log code when an interview fails to load, but the log code is missing. The "test" fails and *our* test fails.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Prioritize constrained random answers tests over pure random answers tests
|
|
2
|
+
|
|
3
|
+
## Context and Problem Statement
|
|
4
|
+
|
|
5
|
+
We could develop constrained randomness first or pure randomness first. Is there a reason to prioritize one over another?
|
|
6
|
+
|
|
7
|
+
## Considered Options
|
|
8
|
+
|
|
9
|
+
- Purely random answers. They fill in fields without any guidance.
|
|
10
|
+
- Constrained random answers tests. They make the test author list all the variable names and all the choices of answers they want to allow for those variables.
|
|
11
|
+
- Hybrid approach. Allow an author to specify some values while letting other values stay completely random.
|
|
12
|
+
|
|
13
|
+
## Decision Outcome
|
|
14
|
+
|
|
15
|
+
Prioritize constrained random answers tests over pure random answers tests. Strangely, they seem to be the more flexible of the two options.
|
|
16
|
+
|
|
17
|
+
## Pros and Cons of the Options
|
|
18
|
+
|
|
19
|
+
The discussion about the general pros and cons of random answers tests will be in the `constrained_random_tests` doc for now.
|
|
20
|
+
|
|
21
|
+
### Purely random answers
|
|
22
|
+
|
|
23
|
+
Pros:
|
|
24
|
+
|
|
25
|
+
- Very little work for test authors to write.
|
|
26
|
+
- Because they're so free-form, they may do a better job finding unexpected behavior that authors didn't think to test for.
|
|
27
|
+
|
|
28
|
+
Cons:
|
|
29
|
+
|
|
30
|
+
- Unless we can find better ways of generating purely random test files for the authors, copying a random answers test to keep permanently might be very challenging.
|
|
31
|
+
- Fields might get nonsensical answers. For example, a very large number for age or a very small number for income.
|
|
32
|
+
- Impossible to use for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
|
|
33
|
+
|
|
34
|
+
### Constrained random answers
|
|
35
|
+
|
|
36
|
+
Pros:
|
|
37
|
+
|
|
38
|
+
- Useful for fields that need very specific answers, like a real lawyer's bar number or a specific file name for uploading exhibits.
|
|
39
|
+
- An author can focus the tests' answers on important user stories. For example, they might want to focus some tests on users that have children while keeping other answers more random.
|
|
40
|
+
- An author might be able to make more efficient random tests. For example, a multiple choice question may have multiple answers that, behind the scenes, set the same value. Authors might do this to help users who are confused about their options give more accurate answers. A purely random test would pick that repeated value too often and waste testing time.
|
|
41
|
+
|
|
42
|
+
Cons:
|
|
43
|
+
|
|
44
|
+
- They take more time to write.
|
|
45
|
+
- They take more work to maintain.
|
|
46
|
+
|
|
47
|
+
<!-- TODO: Add to docs - each `possible_values` cell should contain 1 or more possible answers. -->
|
|
48
|
+
|
|
49
|
+
### Hybrid
|
|
50
|
+
|
|
51
|
+
In future, we believe we can figure out how to let users have partially constrained tests where unspecified fields get purely random answers while constrained fields choose from a set of answers. It would need to be its own Step.
|
|
52
|
+
|
|
53
|
+
Pros:
|
|
54
|
+
|
|
55
|
+
- Offers the most flexibility and control.
|
|
56
|
+
|
|
57
|
+
Cons:
|
|
58
|
+
|
|
59
|
+
- Might be hard to avoid confusion about test behavior.
|
|
60
|
+
- Means developing both methods of testing. That's out of scope for one PR.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const _da_REST = require('./docassemble_api_REST');
|
|
2
|
-
const
|
|
2
|
+
const Log = require('../utils/Log');
|
|
3
3
|
const session_vars = require('../utils/session_vars' );
|
|
4
4
|
const time = require('../utils/time');
|
|
5
5
|
|
|
@@ -61,6 +61,10 @@ const time = require('../utils/time');
|
|
|
61
61
|
let da_i = {};
|
|
62
62
|
module.exports = da_i;
|
|
63
63
|
|
|
64
|
+
const context = `da api`;
|
|
65
|
+
const artifacts_path = session_vars.get_artifacts_path_name();
|
|
66
|
+
const log = new Log({ path: artifacts_path, context });
|
|
67
|
+
|
|
64
68
|
|
|
65
69
|
// Constants
|
|
66
70
|
const DEFAULT_MAX_REQUEST_MS = session_vars.get_setup_timeout_ms();
|
|
@@ -92,7 +96,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
92
96
|
|
|
93
97
|
// Defaults
|
|
94
98
|
let { timeout } = options || { timeout: DEFAULT_MAX_REQUEST_MS };
|
|
95
|
-
log.info({
|
|
99
|
+
log.info({ code: `ALK0166`, context, },
|
|
100
|
+
`Waiting for the docassemble server to respond. Will wait for ${ timeout/1000 } seconds at most.`
|
|
101
|
+
);
|
|
96
102
|
|
|
97
103
|
try {
|
|
98
104
|
let start_time = Date.now()
|
|
@@ -100,7 +106,9 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
100
106
|
let response = await _da_REST.get_dev_id( timeout );
|
|
101
107
|
let end_time = Date.now()
|
|
102
108
|
if ( response && response.data && response.data.id ) {
|
|
103
|
-
log.info({
|
|
109
|
+
log.info({ code: `ALK0167`, context, },
|
|
110
|
+
`The docassemble server responded after ${ (end_time - start_time)/1000 } seconds.`
|
|
111
|
+
);
|
|
104
112
|
return false;
|
|
105
113
|
}
|
|
106
114
|
|
|
@@ -109,17 +117,16 @@ da_i.throw_an_error_if_server_is_not_responding = async function ( options ) {
|
|
|
109
117
|
if ( is_timeout_error( error ) ) {
|
|
110
118
|
// Anything that's waiting for the server to not be busy does not want
|
|
111
119
|
// to move forward without a free server.
|
|
112
|
-
log.info({
|
|
120
|
+
log.info({ code: `ALK0168`, context, },
|
|
121
|
+
`The docassemble server was still busy after ${ timeout/1000 } seconds.`
|
|
122
|
+
);
|
|
113
123
|
} else {
|
|
114
124
|
// Not sure what to do for other errors. It could be a server
|
|
115
125
|
// error, like an 'Access Denied' error, which should be handled.
|
|
116
|
-
log.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
+ `ran into an unexpected error. The error didn't indicate a busy server, though. Error message:\n`
|
|
121
|
-
+ error
|
|
122
|
-
});
|
|
126
|
+
log.warning({ code: `ALK0169`, context, },
|
|
127
|
+
`Checked if the server was busy and ran into a different error`,
|
|
128
|
+
error
|
|
129
|
+
);
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
throw error;
|
|
@@ -187,7 +194,7 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
187
194
|
* to allow for server request.
|
|
188
195
|
*/
|
|
189
196
|
|
|
190
|
-
let
|
|
197
|
+
let auth_feedback = {
|
|
191
198
|
codes: [ 1 ],
|
|
192
199
|
has_auth: null,
|
|
193
200
|
messages: [`API key authorization is not yet determined.`],
|
|
@@ -196,16 +203,16 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
196
203
|
|
|
197
204
|
// Skip this for tests run from ALKilnInThePlayground
|
|
198
205
|
if ( session_vars.get_origin() === 'playground' ) {
|
|
199
|
-
|
|
200
|
-
|
|
206
|
+
auth_feedback.codes.push( 2 );
|
|
207
|
+
auth_feedback.messages.push( `API keys are unnecessary for tests run in the Playground.` );
|
|
201
208
|
}
|
|
202
209
|
|
|
203
210
|
if ( typeof api_key !== `string` ) {
|
|
204
|
-
|
|
205
|
-
|
|
211
|
+
auth_feedback.codes.push( 3 );
|
|
212
|
+
auth_feedback.messages.push( `The given API key is not a string. Type: ${ typeof api_key }`);
|
|
206
213
|
}
|
|
207
214
|
|
|
208
|
-
if (
|
|
215
|
+
if ( auth_feedback.codes.length === 1 ) {
|
|
209
216
|
// Default timeouts
|
|
210
217
|
timeout = timeout || DEFAULT_MAX_REQUEST_MS;
|
|
211
218
|
try {
|
|
@@ -213,29 +220,31 @@ da_i.get_api_key_access_status = async function ({ timeout = null, api_key = nul
|
|
|
213
220
|
api_key,
|
|
214
221
|
server_url: session_vars.get_da_server_url(),
|
|
215
222
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
223
|
+
auth_feedback.has_auth = true;
|
|
224
|
+
auth_feedback.codes = [ 6 ];
|
|
225
|
+
auth_feedback.messages = [ `This API key has at least basic authorization on this server.` ];
|
|
219
226
|
|
|
220
227
|
} catch ( error ) {
|
|
221
|
-
|
|
228
|
+
auth_feedback.error = error;
|
|
222
229
|
if ( error.status === 403 ) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
230
|
+
auth_feedback.has_auth = false;
|
|
231
|
+
auth_feedback.codes = [ 4 ];
|
|
232
|
+
auth_feedback.messages = [ `This API is unverified on this server. It is probably missing or has been deleted.` ];
|
|
226
233
|
} else {
|
|
227
234
|
// Note on 404, non-existent key: 404 == 403 - Since we're using the
|
|
228
235
|
// key itself to get its own info, if it doesn't exist, we'll get 403
|
|
229
|
-
|
|
230
|
-
|
|
236
|
+
auth_feedback.codes = [ 5 ];
|
|
237
|
+
auth_feedback.messages = [ `This API key threw a non-403 error.` ];
|
|
231
238
|
} // ends error status
|
|
232
239
|
} // ends try
|
|
233
|
-
} // ends
|
|
240
|
+
} // ends auth_feedback.codes only has the first code
|
|
234
241
|
|
|
235
|
-
log.debug({
|
|
236
|
-
|
|
242
|
+
log.debug({ code: `ALK0202`, context, },
|
|
243
|
+
`API key - what happened during authentication:`,
|
|
244
|
+
auth_feedback
|
|
245
|
+
);
|
|
237
246
|
|
|
238
|
-
return
|
|
247
|
+
return auth_feedback;
|
|
239
248
|
}; // Ends da_i.get_api_key_access_status()
|
|
240
249
|
|
|
241
250
|
// node -e 'require("./lib/docassemble/docassemble_api_interface.js").delete_project( "create1" )'
|
|
@@ -247,16 +256,19 @@ da_i.delete_project = async function ( delete_options ) {
|
|
|
247
256
|
let { timeout } = delete_options || { timeout: DEFAULT_MAX_REQUEST_MS };
|
|
248
257
|
|
|
249
258
|
let project_name = session_vars.get_project_name()
|
|
250
|
-
log.info({
|
|
259
|
+
log.info({ code: `ALK0170`, context, },
|
|
260
|
+
`Trying to delete Project ${ project_name }`
|
|
261
|
+
);
|
|
251
262
|
|
|
252
263
|
try {
|
|
253
264
|
let response = await _da_REST.delete_project( timeout );
|
|
254
|
-
log.info({
|
|
265
|
+
log.info({ code: `ALK0171`, context, },
|
|
266
|
+
`Deleted Project "${ project_name }"`
|
|
267
|
+
);
|
|
255
268
|
} catch ( error ) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw error;
|
|
269
|
+
log.throw({ code: `ALK0172`, context, error: error },
|
|
270
|
+
`Ran into an error when deleting Project "${ project_name }". See https://docassemble.org/docs/api.html#playground_delete_project.`
|
|
271
|
+
);
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
}; // Ends da_i.delete_project()
|
|
@@ -280,7 +292,9 @@ da_i.quietly_try_to_delete_interviews = async function ({ interviews = {}, api_k
|
|
|
280
292
|
});
|
|
281
293
|
responses.push( sub_responses );
|
|
282
294
|
} else {
|
|
283
|
-
log.debug({
|
|
295
|
+
log.debug({ code: `ALK0191`, context, },
|
|
296
|
+
`There is no interview to delete.`
|
|
297
|
+
);
|
|
284
298
|
}
|
|
285
299
|
}
|
|
286
300
|
return responses;
|
|
@@ -295,9 +309,9 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
|
|
|
295
309
|
let responses = [];
|
|
296
310
|
for ( let api_key of api_keys ) {
|
|
297
311
|
try {
|
|
298
|
-
log.debug({
|
|
299
|
-
|
|
300
|
-
|
|
312
|
+
log.debug({ code: `ALK0188`, context, },
|
|
313
|
+
`Trying to delete the interview with the id ${ interview.uid } and an API key.`
|
|
314
|
+
);
|
|
301
315
|
|
|
302
316
|
// Actual work:
|
|
303
317
|
let response = await _da_REST.delete_interview( timeout, {
|
|
@@ -306,13 +320,15 @@ da_i.quietly_delete_one_interview = async function ( timeout, { interview = {},
|
|
|
306
320
|
});
|
|
307
321
|
responses.push( response );
|
|
308
322
|
|
|
309
|
-
log.debug({
|
|
310
|
-
|
|
311
|
-
|
|
323
|
+
log.debug({ code: `ALK0189`, context, },
|
|
324
|
+
`No error when trying to delete interview ${ interview.uid } at ${ interview.i }.`
|
|
325
|
+
);
|
|
312
326
|
} catch ( error ) {
|
|
313
327
|
error.interview = interview;
|
|
314
|
-
|
|
315
|
-
|
|
328
|
+
log.debug({ code: `ALK0190`, context, },
|
|
329
|
+
`Error when deleting the interview. See https://docassemble.org/docs/api.html#session_delete.`,
|
|
330
|
+
error
|
|
331
|
+
);
|
|
316
332
|
}
|
|
317
333
|
} // ends for api keys
|
|
318
334
|
return responses;
|
|
@@ -347,7 +363,9 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
|
|
|
347
363
|
if ( interview && interview.uid ) {
|
|
348
364
|
for ( let api_key of api_keys ) {
|
|
349
365
|
try {
|
|
350
|
-
log.debug({
|
|
366
|
+
log.debug({ code: `ALK0192`, context, },
|
|
367
|
+
`Trying to get the interview with the id ${ interview.uid } and an API key.`
|
|
368
|
+
);
|
|
351
369
|
|
|
352
370
|
// Actual work:
|
|
353
371
|
let response = await _da_REST.get_interview( timeout, {
|
|
@@ -356,20 +374,28 @@ da_i.get_interview = async function ({ interview = {}, api_keys = [], options =
|
|
|
356
374
|
});
|
|
357
375
|
|
|
358
376
|
if ( response && response.data && response.data.items && response.data.items[0] && response.data.items[0].session ) {
|
|
359
|
-
log.debug({
|
|
377
|
+
log.debug({ code: `ALK0193`, context, },
|
|
378
|
+
`Status ${ response.status }. Got ${ response.data.items[0].session }.`
|
|
379
|
+
);
|
|
360
380
|
return response.data.items[0];
|
|
361
381
|
} else {
|
|
362
|
-
log.debug({
|
|
382
|
+
log.debug({ code: `ALK0194`, context, },
|
|
383
|
+
`Status ${ response.status }. Cannot find ${ interview.uid }`
|
|
384
|
+
);
|
|
363
385
|
}
|
|
364
386
|
|
|
365
387
|
} catch ( error ) {
|
|
366
388
|
error.interview = interview;
|
|
367
|
-
|
|
368
|
-
|
|
389
|
+
log.debug({ code: `ALK0195`, context, },
|
|
390
|
+
`${ error.status || `No error.status.` } ${ error.statusText || error.data || `No error.statusText.` } Ran into an error when getting the interview ${ interview.uid }. See https://docassemble.org/docs/api.html#interviews.`,
|
|
391
|
+
error
|
|
392
|
+
);
|
|
369
393
|
}
|
|
370
394
|
} // ends for api_keys
|
|
371
395
|
} else {
|
|
372
|
-
log.debug({
|
|
396
|
+
log.debug({ code: `ALK0196`, context, },
|
|
397
|
+
`There is no interview to get.`
|
|
398
|
+
);
|
|
373
399
|
}
|
|
374
400
|
|
|
375
401
|
return null;
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const session_vars = require('../utils/session_vars');
|
|
4
|
-
const
|
|
4
|
+
const Log = require('../utils/Log');
|
|
5
5
|
const fg = require('fast-glob');
|
|
6
6
|
|
|
7
|
+
const context = `server_install`;
|
|
8
|
+
|
|
7
9
|
const server_install = async () => {
|
|
10
|
+
/** For packages we are installing on a temporary GitHub docassemble server.
|
|
11
|
+
*
|
|
12
|
+
* - Store that the package is being installed globally on a server as
|
|
13
|
+
* opposed to on a specific author's account (their Playground).
|
|
14
|
+
* - In the repo, find and store the path to directory that stores the
|
|
15
|
+
* package itself instead of the whole repo. For example, EvictionDefense
|
|
16
|
+
* in the docassemble-EvictionDefense repo.
|
|
17
|
+
* */
|
|
8
18
|
session_vars.save_install_method(`server`);
|
|
9
19
|
|
|
10
20
|
// Get path to folders inside repo main "docassemble" folder
|
|
@@ -23,20 +33,33 @@ const server_install = async () => {
|
|
|
23
33
|
// Get names of all folders in repo "docassemble" folder
|
|
24
34
|
let chosen_folder = folders[0];
|
|
25
35
|
if ( folders.length === 0 ) {
|
|
26
|
-
let msg = `There are 0 folders at ${path}. Stopping tests.`;
|
|
27
|
-
log.
|
|
28
|
-
|
|
36
|
+
let msg = `ALKiln cannot find the package's files. There are 0 folders at "${ path }". Stopping tests.`;
|
|
37
|
+
log.throw({ code: `ALK0044`, context,
|
|
38
|
+
error: new ReferenceError( msg ) // no logs
|
|
39
|
+
});
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
if ( folders.length > 1 ) {
|
|
32
|
-
log.warn({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
43
|
+
log.warn({ code: `ALK0045`, context, },
|
|
44
|
+
`There are ${ folders.length } folders at "${ path }": "${ folders.join('", "') }". ALKiln chose to use the folder "${ chosen_folder }".`
|
|
45
|
+
);
|
|
46
|
+
} else {
|
|
47
|
+
log.info({ code: `ALK0218`, context, },
|
|
48
|
+
`ALKiln found the package folder at "${ chosen_folder }".`
|
|
49
|
+
);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
52
|
session_vars.save_repo_folder_name( chosen_folder );
|
|
40
53
|
}; // Ends server_install();
|
|
41
54
|
|
|
55
|
+
// 1. session_vars - make sure all the required env vars exist
|
|
56
|
+
session_vars.validateEnvironment();
|
|
57
|
+
|
|
58
|
+
// 2. Prepare logs
|
|
59
|
+
// Local devs: This will be separate from your test paths
|
|
60
|
+
// unless you pass a `path` argument into the cucumber run command
|
|
61
|
+
const argv = require(`minimist`)(process.argv.slice(2));
|
|
62
|
+
const log = new Log({ path: argv.path, context, });
|
|
63
|
+
|
|
64
|
+
// 3. run
|
|
42
65
|
server_install();
|
package/lib/docassemble/setup.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// TODO: Rename to playground_install?
|
|
4
|
-
|
|
5
3
|
const child_process = require(`child_process`);
|
|
6
4
|
|
|
7
|
-
const
|
|
5
|
+
const Log = require(`../utils/Log`);
|
|
8
6
|
const session_vars = require(`../utils/session_vars` );
|
|
7
|
+
const files = require(`../utils/files` );
|
|
8
|
+
const time = require(`../utils/time` );
|
|
9
|
+
|
|
10
|
+
const context = `setup`;
|
|
9
11
|
|
|
10
12
|
const setup = async () => {
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
/** This is for a package being installed in an author's account (Playground)
|
|
14
|
+
* on their docassemble server. Using the author's
|
|
15
|
+
* DOCASSEMBLE_DEVELOPER_API_KEY secret, create a new Project, pull code
|
|
16
|
+
* into that project, and, if needed, wait for the server to reload.
|
|
17
|
+
*/
|
|
16
18
|
|
|
17
19
|
// Make and save project name
|
|
18
20
|
let project_name = session_vars.get_project_base_name().replace(/[^A-Za-z0-9]/g, '') + `${Date.now()}`;
|
|
19
21
|
session_vars.save_project_name( project_name );
|
|
20
22
|
|
|
21
|
-
|
|
23
|
+
const starting_msg = `Starting to upload this package to `
|
|
22
24
|
+ `a new Project called ${project_name} in the Playground `
|
|
23
|
-
+ `of your server's testing account. It may take a couple of minutes
|
|
25
|
+
+ `of your server's testing account. It may take a couple of minutes.`;
|
|
26
|
+
log.info({ code: `ALK0046`, context, },
|
|
27
|
+
starting_msg
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// TODO: 1. Start listening to the normal console here (for debug logging)
|
|
24
31
|
|
|
25
32
|
// dainstall . --apiurl https://site.org --apikey $API_KEY --playground --project=name
|
|
26
33
|
// I think this won't run on windows? Why do I think this? Where on the
|
|
@@ -34,29 +41,37 @@ const setup = async () => {
|
|
|
34
41
|
`--playground`, `--project=${ project_name }`
|
|
35
42
|
],
|
|
36
43
|
// Prints messages from the process to the console while it's running
|
|
44
|
+
// result.stderr, result.stdout, etc. will be `null`
|
|
37
45
|
{ stdio: 'inherit', }
|
|
38
46
|
);
|
|
39
47
|
|
|
40
|
-
//
|
|
41
|
-
// the response to find out the error code
|
|
48
|
+
// TODO: 2. Stop listening to the normal console here
|
|
42
49
|
|
|
50
|
+
// 403 is invalid API key, but I'm not sure how to output
|
|
51
|
+
// the response to find out the error code. stderr is `null`.
|
|
43
52
|
if ( result.status !== 0 ) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
const install_err_msg = `Status code ${ result.status }. Could not `
|
|
54
|
+
+ `dainstall this package on your server. See above for more details.`;
|
|
55
|
+
log.throw({ code: `ALK0047`, context,
|
|
56
|
+
before: `☝️ ☝️ ☝️ Error data ☝️ ☝️ ☝️\n`,
|
|
57
|
+
error: new Error(`Error while installing project on your docassemble server`) },
|
|
58
|
+
install_err_msg
|
|
59
|
+
);
|
|
50
60
|
}
|
|
51
61
|
|
|
52
|
-
log.success({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
pre: `ALKiln created a Project in your Playground named "${ project_name }"!`
|
|
56
|
-
});
|
|
62
|
+
log.success({ code: `ALK0048`, context, },
|
|
63
|
+
`ALKiln created a Project in your Playground named "${ project_name }"!`
|
|
64
|
+
);
|
|
57
65
|
}; // Ends setup();
|
|
58
66
|
|
|
59
|
-
|
|
60
|
-
// Before everything, make sure all the required env vars exist
|
|
67
|
+
// 1. session_vars - make sure all the required env vars exist
|
|
61
68
|
session_vars.validateEnvironment();
|
|
69
|
+
|
|
70
|
+
// 2. Prepare logs
|
|
71
|
+
// Local devs: This will be separate from your test paths unless you pass a
|
|
72
|
+
// `path` argument into the cucumber run command
|
|
73
|
+
const argv = require(`minimist`)(process.argv.slice(2));
|
|
74
|
+
const log = new Log({ path: argv.path, context, });
|
|
75
|
+
|
|
76
|
+
// 3. run
|
|
62
77
|
setup();
|
|
@@ -1,26 +1,49 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const da_i = require('./docassemble_api_interface');
|
|
4
|
-
const
|
|
5
|
-
const
|
|
4
|
+
const session_vars = require('../utils/session_vars');
|
|
5
|
+
const Log = require('../utils/Log');
|
|
6
|
+
|
|
7
|
+
const context = `takedown`;
|
|
6
8
|
|
|
7
9
|
const takedown = async () => {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
/** In the docassemble testing account identified by the
|
|
11
|
+
* DOCASSEMBLE_DEVELOPER_API_KEY secret, delete the Project created for this
|
|
12
|
+
* test. For local development, clean up Project name in the runtime_config
|
|
13
|
+
* file.
|
|
14
|
+
* */
|
|
15
|
+
log.info({ code: `ALK0177`, context, },
|
|
16
|
+
`Trying to remove the docassemble Project from the testing account.`
|
|
17
|
+
);
|
|
13
18
|
// Explicit try-catch prevents "Unhandled promise rejection error"
|
|
14
19
|
// avoids showing confusing node errors to users that they shouldn't have to fix
|
|
15
20
|
try {
|
|
16
21
|
await da_i.delete_project();
|
|
17
22
|
session_vars.delete_project_name();
|
|
18
|
-
log.
|
|
23
|
+
log.info({ code: `ALK0178`, context, },
|
|
24
|
+
`Test takedown finished successfully!`
|
|
25
|
+
);
|
|
19
26
|
} catch (error) {
|
|
20
|
-
log.
|
|
21
|
-
|
|
27
|
+
log.throw({ code: `ALK0179`, context, error: error },
|
|
28
|
+
`Unable to delete project`,
|
|
29
|
+
);
|
|
30
|
+
// we don't re-throw the exception, so make sure to exit
|
|
31
|
+
// with 1 to let GitHub Actions know the script failed
|
|
22
32
|
process.exitCode = 1;
|
|
23
33
|
}
|
|
34
|
+
|
|
35
|
+
// TODO: ? Build the final setup, run, & takedown verbose log
|
|
36
|
+
// file in here. Add to it in the GitHub action ?
|
|
24
37
|
};
|
|
25
38
|
|
|
39
|
+
// 1. session_vars - make sure all the required env vars exist
|
|
40
|
+
session_vars.validateEnvironment();
|
|
41
|
+
|
|
42
|
+
// 2. Prepare logs
|
|
43
|
+
// Local devs: This will be separate from your test artifacts paths unless you
|
|
44
|
+
// pass a `path` argument into the `cucumber` run command
|
|
45
|
+
const argv = require(`minimist`)( process.argv.slice(2) );
|
|
46
|
+
const log = new Log({ path: argv.path, context, });
|
|
47
|
+
|
|
48
|
+
// 3. run
|
|
26
49
|
takedown();
|
package/lib/globals.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
let globals = {
|
|
2
|
+
// File name prefix ALKiln can use to identify random answer generator files
|
|
3
|
+
generators_prefix: `alkiln_random`,
|
|
4
|
+
generated_files_folder_name: `_alkiln_generated`,
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = globals;
|
|
8
|
+
|
|
9
|
+
// Would this be clearer as a config file instead?
|
|
10
|
+
// If not, change name to `constants`?
|